node-pg-migrate
Advanced tools
Comparing version
@@ -55,2 +55,3 @@ #!/usr/bin/env node | ||
const migrationsDirArg = "migrations-dir"; | ||
const useGlobArg = "use-glob"; | ||
const migrationsTableArg = "migrations-table"; | ||
@@ -88,5 +89,10 @@ const migrationsSchemaArg = "migrations-schema"; | ||
defaultDescription: '"migrations"', | ||
describe: "The directory containing your migration files", | ||
describe: `The directory name or glob pattern containing your migration files (resolved from cwd()). When using glob pattern, "${useGlobArg}" must be used as well`, | ||
type: "string" | ||
}, | ||
[useGlobArg]: { | ||
defaultDescription: "false", | ||
describe: `Use glob to find migration files. This will use "${migrationsDirArg}" _and_ "${ignorePatternArg}" to glob-search for migration files.`, | ||
type: "boolean" | ||
}, | ||
[migrationsTableArg]: { | ||
@@ -132,3 +138,3 @@ alias: "t", | ||
defaultDescription: '"\\..*"', | ||
describe: "Regex pattern for file names to ignore", | ||
describe: `Regex or glob pattern for migration files to be ignored. When using glob pattern, "${useGlobArg}" must be used as well`, | ||
type: "string" | ||
@@ -239,2 +245,3 @@ }, | ||
let MIGRATIONS_DIR = argv[migrationsDirArg]; | ||
let USE_GLOB = argv[useGlobArg]; | ||
let DB_CONNECTION = process.env[argv[databaseUrlVarArg]]; | ||
@@ -413,2 +420,3 @@ let IGNORE_PATTERN = argv[ignorePatternArg]; | ||
MIGRATIONS_DIR ??= (0, import_node_path.join)((0, import_node_process.cwd)(), "migrations"); | ||
USE_GLOB ??= false; | ||
MIGRATIONS_FILE_LANGUAGE ??= "js"; | ||
@@ -418,3 +426,2 @@ MIGRATIONS_FILENAME_FORMAT ??= "timestamp"; | ||
SCHEMA ??= ["public"]; | ||
IGNORE_PATTERN ??= "\\..*"; | ||
CHECK_ORDER ??= true; | ||
@@ -498,2 +505,3 @@ VERBOSE ??= true; | ||
dir: MIGRATIONS_DIR, | ||
useGlob: USE_GLOB, | ||
ignorePattern: IGNORE_PATTERN, | ||
@@ -500,0 +508,0 @@ schema: SCHEMA, |
@@ -51,2 +51,3 @@ #!/usr/bin/env node | ||
const migrationsDirArg = 'migrations-dir'; | ||
const useGlobArg = 'use-glob'; | ||
const migrationsTableArg = 'migrations-table'; | ||
@@ -88,5 +89,10 @@ const migrationsSchemaArg = 'migrations-schema'; | ||
defaultDescription: '"migrations"', | ||
describe: 'The directory containing your migration files', | ||
describe: `The directory name or glob pattern containing your migration files (resolved from cwd()). When using glob pattern, "${useGlobArg}" must be used as well`, | ||
type: 'string', | ||
}, | ||
[useGlobArg]: { | ||
defaultDescription: 'false', | ||
describe: `Use glob to find migration files. This will use "${migrationsDirArg}" _and_ "${ignorePatternArg}" to glob-search for migration files.`, | ||
type: 'boolean', | ||
}, | ||
[migrationsTableArg]: { | ||
@@ -133,3 +139,3 @@ alias: 't', | ||
defaultDescription: '"\\..*"', | ||
describe: 'Regex pattern for file names to ignore', | ||
describe: `Regex or glob pattern for migration files to be ignored. When using glob pattern, "${useGlobArg}" must be used as well`, | ||
type: 'string', | ||
@@ -259,2 +265,3 @@ }, | ||
let MIGRATIONS_DIR = argv[migrationsDirArg]; | ||
let USE_GLOB = argv[useGlobArg]; | ||
let DB_CONNECTION: string | ConnectionParameters | ClientConfig | undefined = | ||
@@ -476,2 +483,3 @@ process.env[argv[databaseUrlVarArg]]; | ||
MIGRATIONS_DIR ??= join(cwd(), 'migrations'); | ||
USE_GLOB ??= false; | ||
MIGRATIONS_FILE_LANGUAGE ??= 'js'; | ||
@@ -481,3 +489,2 @@ MIGRATIONS_FILENAME_FORMAT ??= 'timestamp'; | ||
SCHEMA ??= ['public']; | ||
IGNORE_PATTERN ??= '\\..*'; | ||
CHECK_ORDER ??= true; | ||
@@ -592,2 +599,3 @@ VERBOSE ??= true; | ||
dir: MIGRATIONS_DIR!, | ||
useGlob: USE_GLOB, | ||
ignorePattern: IGNORE_PATTERN, | ||
@@ -594,0 +602,0 @@ schema: SCHEMA, |
@@ -25,4 +25,53 @@ import type { QueryResult } from 'pg'; | ||
} & (CreateOptionsTemplate | CreateOptionsDefault); | ||
export declare function loadMigrationFiles(dir: string, ignorePattern?: string): Promise<string[]>; | ||
export declare function getTimestamp(logger: Logger, filename: string): number; | ||
interface LoadMigrationFilesOptions { | ||
/** | ||
* Regex pattern for file names to ignore (ignores files starting with `.` by default). | ||
* Alternatively, provide a [glob](https://www.npmjs.com/package/glob) pattern or | ||
* an array of glob patterns and set `isGlob = true` | ||
* | ||
* Note: enabling glob will read both, `dir` _and_ `ignorePattern` as glob patterns | ||
*/ | ||
ignorePattern?: string | string[]; | ||
/** | ||
* Use [glob](https://www.npmjs.com/package/glob) to find migration files. | ||
* This will use `dir` _and_ `options.ignorePattern` to glob-search for migration files. | ||
* | ||
* @default: false | ||
*/ | ||
useGlob?: boolean; | ||
/** | ||
* Redirect messages to this logger object, rather than `console`. | ||
*/ | ||
logger?: Logger; | ||
} | ||
/** | ||
* Reads files from `dir`, sorts them and returns an array of their absolute paths. | ||
* When not using globs, files are sorted by their numeric prefix values first. 17 digit numbers are interpreted as utc date and converted to the number representation of that date. | ||
* Glob matches are sorted via String.localeCompare with ignored punctuation. | ||
* | ||
* @param dir The directory containing your migration files. This path is resolved from `cwd()`. | ||
* Alternatively, provide a [glob](https://www.npmjs.com/package/glob) pattern or | ||
* an array of glob patterns and set `options.useGlob = true` | ||
* | ||
* Note: enabling glob will read both, `dir` _and_ `options.ignorePattern` as glob patterns | ||
* @param options | ||
* @returns Array of absolute paths | ||
*/ | ||
export declare function getMigrationFilePaths( | ||
/** | ||
* The directory containing your migration files. This path is resolved from `cwd()`. | ||
* Alternatively, provide a [glob](https://www.npmjs.com/package/glob) pattern or | ||
* an array of glob patterns and set `options.useGlob = true` | ||
* | ||
* Note: enabling glob will read both, `dir` _and_ `options.ignorePattern` as glob patterns | ||
*/ | ||
dir: string | string[], options?: LoadMigrationFilesOptions): Promise<string[]>; | ||
/** | ||
* extracts numeric value from everything in `filename` before `SEPARATOR`. | ||
* 17 digit numbers are interpreted as utc date and converted to the number representation of that date. | ||
* @param filename filename to extract the prefix from | ||
* @param logger Redirect messages to this logger object, rather than `console`. | ||
* @returns numeric value of the filename prefix (everything before `SEPARATOR`). | ||
*/ | ||
export declare function getNumericPrefix(filename: string, logger?: Logger): number; | ||
export declare class Migration implements RunMigration { | ||
@@ -46,1 +95,2 @@ static create(name: string, directory: string, options?: CreateOptions): Promise<string>; | ||
} | ||
export {}; |
@@ -35,6 +35,7 @@ "use strict"; | ||
Migration: () => Migration, | ||
getTimestamp: () => getTimestamp, | ||
loadMigrationFiles: () => loadMigrationFiles | ||
getMigrationFilePaths: () => getMigrationFilePaths, | ||
getNumericPrefix: () => getNumericPrefix | ||
}); | ||
module.exports = __toCommonJS(migration_exports); | ||
var import_glob = require("glob"); | ||
var import_node_fs = require("node:fs"); | ||
@@ -52,7 +53,35 @@ var import_promises = require("node:fs/promises"); | ||
const SEPARATOR = "_"; | ||
async function loadMigrationFiles(dir, ignorePattern) { | ||
function localeCompareStringsNumerically(a, b) { | ||
return a.localeCompare(b, void 0, { | ||
usage: "sort", | ||
numeric: true, | ||
sensitivity: "variant", | ||
ignorePunctuation: true | ||
}); | ||
} | ||
function compareFileNamesByTimestamp(a, b, logger) { | ||
const aTimestamp = getNumericPrefix(a, logger); | ||
const bTimestamp = getNumericPrefix(b, logger); | ||
return aTimestamp - bTimestamp; | ||
} | ||
async function getMigrationFilePaths(dir, options = {}) { | ||
const { ignorePattern, useGlob = false, logger } = options; | ||
if (useGlob) { | ||
const globMatches = await (0, import_glob.glob)(dir, { ignore: ignorePattern, nodir: true }); | ||
return globMatches.sort(localeCompareStringsNumerically); | ||
} | ||
if (Array.isArray(dir) || Array.isArray(ignorePattern)) { | ||
throw new TypeError( | ||
'Options "dir" and "ignorePattern" can only be arrays when "useGlob" is true' | ||
); | ||
} | ||
const ignoreRegexp = new RegExp( | ||
(ignorePattern == null ? void 0 : ignorePattern.length) ? `^${ignorePattern}$` : "^\\..*" | ||
); | ||
const dirContent = await (0, import_promises.readdir)(`${dir}/`, { withFileTypes: true }); | ||
const files = dirContent.map((file) => file.isFile() || file.isSymbolicLink() ? file.name : null).filter((file) => Boolean(file)).sort(); | ||
const filter = new RegExp(`^(${ignorePattern})$`); | ||
return ignorePattern === void 0 ? files : files.filter((i) => !filter.test(i)); | ||
return dirContent.filter( | ||
(dirent) => (dirent.isFile() || dirent.isSymbolicLink()) && !ignoreRegexp.test(dirent.name) | ||
).sort( | ||
(a, b) => compareFileNamesByTimestamp(a.name, b.name, logger) || localeCompareStringsNumerically(a.name, b.name) | ||
).map((dirent) => (0, import_node_path.resolve)(dir, dirent.name)); | ||
} | ||
@@ -64,3 +93,3 @@ function getSuffixFromFileName(fileName) { | ||
try { | ||
const files = await loadMigrationFiles(dir, ignorePattern); | ||
const files = await getMigrationFilePaths(dir, { ignorePattern }); | ||
return files.length > 0 ? getSuffixFromFileName(files[files.length - 1]) : void 0; | ||
@@ -71,3 +100,3 @@ } catch { | ||
} | ||
function getTimestamp(logger, filename) { | ||
function getNumericPrefix(filename, logger = console) { | ||
const prefix = filename.split(SEPARATOR)[0]; | ||
@@ -112,3 +141,3 @@ if (prefix && /^\d+$/.test(prefix)) { | ||
this.name = (0, import_node_path.basename)(migrationPath, (0, import_node_path.extname)(migrationPath)); | ||
this.timestamp = getTimestamp(logger, this.name); | ||
this.timestamp = getNumericPrefix(this.name, logger); | ||
this.up = up; | ||
@@ -225,4 +254,4 @@ this.down = down; | ||
Migration, | ||
getTimestamp, | ||
loadMigrationFiles | ||
getMigrationFilePaths, | ||
getNumericPrefix | ||
}); |
@@ -48,6 +48,9 @@ "use strict"; | ||
let shorthands = {}; | ||
const files = await (0, import_migration.loadMigrationFiles)(options.dir, options.ignorePattern); | ||
const absoluteFilePaths = await (0, import_migration.getMigrationFilePaths)(options.dir, { | ||
ignorePattern: options.ignorePattern, | ||
useGlob: options.useGlob, | ||
logger | ||
}); | ||
const migrations = await Promise.all( | ||
files.map(async (file) => { | ||
const filePath = (0, import_node_path.resolve)(options.dir, file); | ||
absoluteFilePaths.map(async (filePath) => { | ||
const actions = (0, import_node_path.extname)(filePath) === ".sql" ? await (0, import_sqlMigration.default)(filePath) : (0, import_node_module.createRequire)((0, import_node_path.resolve)("_"))(filePath); | ||
@@ -67,9 +70,3 @@ shorthands = { ...shorthands, ...actions.shorthands }; | ||
); | ||
return migrations.sort((m1, m2) => { | ||
const compare = m1.timestamp - m2.timestamp; | ||
if (compare !== 0) { | ||
return compare; | ||
} | ||
return m1.name.localeCompare(m2.name); | ||
}); | ||
return migrations; | ||
} catch (error) { | ||
@@ -76,0 +73,0 @@ throw new Error(`Can't get migration files: ${error.stack}`); |
@@ -694,6 +694,19 @@ import type { ClientBase, ClientConfig, QueryArrayConfig, QueryArrayResult, QueryConfig, QueryResult } from 'pg'; | ||
/** | ||
* The directory containing your migration files. | ||
* The directory containing your migration files. This path is resolved from `cwd()`. | ||
* Alternatively, provide a [glob](https://www.npmjs.com/package/glob) pattern or | ||
* an array of glob patterns and set `useGlob = true` | ||
* | ||
* Note: enabling glob will read both, `dir` _and_ `ignorePattern` as glob patterns | ||
*/ | ||
dir: string; | ||
dir: string | string[]; | ||
/** | ||
* Use [glob](https://www.npmjs.com/package/glob) to find migration files. | ||
* This will use `dir` _and_ `ignorePattern` to glob-search for migration files. | ||
* | ||
* Note: enabling glob will read both, `dir` _and_ `ignorePattern` as glob patterns | ||
* | ||
* @default false | ||
*/ | ||
useGlob?: boolean; | ||
/** | ||
* Check order of migrations before running them. | ||
@@ -716,4 +729,8 @@ */ | ||
* Regex pattern for file names to ignore (ignores files starting with `.` by default). | ||
* Alternatively, provide a [glob](https://www.npmjs.com/package/glob) pattern or | ||
* an array of glob patterns and set `isGlob = true` | ||
* | ||
* Note: enabling glob will read both, `dir` _and_ `ignorePattern` as glob patterns | ||
*/ | ||
ignorePattern?: string; | ||
ignorePattern?: string | string[]; | ||
/** | ||
@@ -720,0 +737,0 @@ * Run only migration with this name. |
{ | ||
"name": "node-pg-migrate", | ||
"version": "7.7.0-rc.0", | ||
"version": "7.7.0", | ||
"description": "PostgreSQL database migration management tool for node.js", | ||
@@ -79,2 +79,3 @@ "bin": { | ||
"dependencies": { | ||
"glob": "11.0.0", | ||
"yargs": "~17.7.0" | ||
@@ -86,10 +87,10 @@ }, | ||
"@eslint-types/unicorn": "52.0.0", | ||
"@types/config": "3.3.4", | ||
"@types/node": "18.19.47", | ||
"@types/pg": "8.11.8", | ||
"@types/config": "3.3.5", | ||
"@types/node": "18.19.51", | ||
"@types/pg": "8.11.10", | ||
"@types/yargs": "17.0.33", | ||
"@typescript-eslint/eslint-plugin": "7.18.0", | ||
"@typescript-eslint/parser": "7.18.0", | ||
"@vitest/coverage-v8": "2.0.5", | ||
"@vitest/ui": "2.0.5", | ||
"@vitest/coverage-v8": "2.1.1", | ||
"@vitest/ui": "2.1.1", | ||
"config": "3.3.12", | ||
@@ -99,3 +100,3 @@ "cross-env": "7.0.3", | ||
"dotenv-expand": "11.0.6", | ||
"eslint": "8.57.0", | ||
"eslint": "8.57.1", | ||
"eslint-config-prettier": "9.1.0", | ||
@@ -108,13 +109,13 @@ "eslint-define-config": "2.1.0", | ||
"node-pg-migrate": "file:.", | ||
"npm-run-all2": "6.2.2", | ||
"pg": "8.12.0", | ||
"npm-run-all2": "6.2.3", | ||
"pg": "8.13.0", | ||
"prettier": "3.3.3", | ||
"prettier-plugin-organize-imports": "4.0.0", | ||
"prettier-plugin-organize-imports": "4.1.0", | ||
"rimraf": "6.0.1", | ||
"ts-node": "10.9.2", | ||
"tsup": "8.2.4", | ||
"tsx": "4.19.0", | ||
"typescript": "5.5.4", | ||
"tsup": "8.3.0", | ||
"tsx": "4.19.1", | ||
"typescript": "5.6.2", | ||
"vitepress": "1.3.4", | ||
"vitest": "2.0.5" | ||
"vitest": "2.1.1" | ||
}, | ||
@@ -121,0 +122,0 @@ "peerDependencies": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
540166
1.21%121652
19.78%13382
1.08%0
-100%4
33.33%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added