@open-mercato/cli 0.4.6-develop-e321a4e2a1 → 0.4.6-main-24e64eef39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/lib/utils.js CHANGED
@@ -48,7 +48,7 @@ function collectStructureEntries(target, base, acc) {
48
48
  }
49
49
  }
50
50
  function calculateStructureChecksum(paths) {
51
- const normalized = Array.from(new Set(paths.map((p) => path.resolve(p)))).sort((a, b) => a.localeCompare(b));
51
+ const normalized = Array.from(new Set(paths.map((p) => path.resolve(p)))).sort();
52
52
  const entries = [];
53
53
  for (const target of normalized) {
54
54
  if (!fs.existsSync(target)) {
@@ -123,7 +123,7 @@ function toVar(s) {
123
123
  return s.replace(/[^a-zA-Z0-9_]/g, "_");
124
124
  }
125
125
  function toSnake(s) {
126
- return s.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/\W+/g, "_").replace(/_{2,}/g, "_").replace(/(?:^_+|_+$)/g, "").toLowerCase();
126
+ return s.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/\W+/g, "_").replace(/_{2,}/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
127
127
  }
128
128
  async function moduleHasExport(filePath, exportName) {
129
129
  try {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/utils.ts"],
4
- "sourcesContent": ["import fs from 'node:fs'\nimport path from 'node:path'\nimport crypto from 'node:crypto'\nimport { pathToFileURL } from 'node:url'\n\nexport type ChecksumRecord = {\n content: string\n structure: string\n}\n\nexport interface GeneratorResult {\n filesWritten: string[]\n filesUnchanged: string[]\n errors: string[]\n}\n\nexport function calculateChecksum(content: string): string {\n return crypto.createHash('md5').update(content).digest('hex')\n}\n\nexport function readChecksumRecord(filePath: string): ChecksumRecord | null {\n if (!fs.existsSync(filePath)) {\n return null\n }\n try {\n const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8')) as Partial<ChecksumRecord>\n if (parsed && typeof parsed.content === 'string' && typeof parsed.structure === 'string') {\n return { content: parsed.content, structure: parsed.structure }\n }\n } catch {\n // Invalid checksum file\n }\n return null\n}\n\nexport function writeChecksumRecord(filePath: string, record: ChecksumRecord): void {\n fs.writeFileSync(filePath, JSON.stringify(record) + '\\n')\n}\n\nfunction collectStructureEntries(target: string, base: string, acc: string[]): void {\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(target, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))\n } catch (err) {\n acc.push(`error:${path.relative(base, target)}:${(err as Error).message}`)\n return\n }\n\n for (const entry of entries) {\n const fullPath = path.join(target, entry.name)\n const rel = path.relative(base, fullPath)\n try {\n const stat = fs.statSync(fullPath)\n if (entry.isDirectory()) {\n acc.push(`dir:${rel}:${stat.mtimeMs}`)\n collectStructureEntries(fullPath, base, acc)\n } else if (entry.isFile()) {\n acc.push(`file:${rel}:${stat.size}:${stat.mtimeMs}`)\n } else {\n acc.push(`other:${rel}:${stat.mtimeMs}`)\n }\n } catch {\n // File was deleted between readdir and stat - skip it\n continue\n }\n }\n}\n\nexport function calculateStructureChecksum(paths: string[]): string {\n const normalized = Array.from(new Set(paths.map((p) => path.resolve(p)))).sort((a, b) => a.localeCompare(b))\n const entries: string[] = []\n for (const target of normalized) {\n if (!fs.existsSync(target)) {\n entries.push(`missing:${target}`)\n continue\n }\n const stat = fs.statSync(target)\n entries.push(`${stat.isDirectory() ? 'dir' : 'file'}:${target}:${stat.mtimeMs}`)\n if (stat.isDirectory()) collectStructureEntries(target, target, entries)\n }\n return calculateChecksum(entries.join('\\n'))\n}\n\nexport function writeIfChanged(\n filePath: string,\n content: string,\n checksumPath?: string,\n structureChecksum?: string\n): boolean {\n const newChecksum = calculateChecksum(content)\n\n if (checksumPath) {\n const existingRecord = readChecksumRecord(checksumPath)\n const newRecord: ChecksumRecord = {\n content: newChecksum,\n structure: structureChecksum || '',\n }\n\n const shouldWrite =\n !existingRecord ||\n existingRecord.content !== newRecord.content ||\n (structureChecksum && existingRecord.structure !== newRecord.structure)\n\n if (shouldWrite) {\n ensureDir(filePath)\n fs.writeFileSync(filePath, content)\n writeChecksumRecord(checksumPath, newRecord)\n return true\n }\n return false\n }\n\n // Simple comparison without checksum file\n if (fs.existsSync(filePath)) {\n const existing = fs.readFileSync(filePath, 'utf8')\n if (existing === content) {\n return false\n }\n }\n\n ensureDir(filePath)\n fs.writeFileSync(filePath, content)\n return true\n}\n\nexport function ensureDir(filePath: string): void {\n fs.mkdirSync(path.dirname(filePath), { recursive: true })\n}\n\n// Allowed path substrings for safe deletion. Include both POSIX and Windows separators.\nconst ALLOWED_RIMRAF_PATTERNS = [\n '/generated/',\n '/dist/',\n '/.mercato/',\n '/entities/',\n '\\\\generated\\\\',\n '\\\\dist\\\\',\n '\\\\.mercato\\\\',\n '\\\\entities\\\\',\n]\n\nexport function rimrafDir(dir: string, opts?: { allowedPatterns?: string[] }): void {\n if (!fs.existsSync(dir)) return\n\n // Safety check: only allow deletion within known safe directories\n const resolved = path.resolve(dir)\n const allowed = opts?.allowedPatterns ?? ALLOWED_RIMRAF_PATTERNS\n\n // Normalize resolved path to support matching against both POSIX and Windows patterns\n const normalized = {\n posix: resolved.replace(/\\\\/g, '/'),\n win: resolved.replace(/\\//g, '\\\\'),\n }\n\n if (!allowed.some((pattern) => normalized.posix.includes(pattern) || normalized.win.includes(pattern))) {\n throw new Error(`Refusing to delete directory outside allowed paths: ${resolved}. Allowed patterns: ${allowed.join(', ')}`)\n }\n\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const p = path.join(dir, entry.name)\n if (entry.isDirectory()) rimrafDir(p, opts)\n else fs.unlinkSync(p)\n }\n fs.rmdirSync(dir)\n}\n\nexport function toVar(s: string): string {\n return s.replace(/[^a-zA-Z0-9_]/g, '_')\n}\n\nexport function toSnake(s: string): string {\n return s\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .replace(/\\W+/g, '_')\n .replace(/_{2,}/g, '_')\n .replace(/(?:^_+|_+$)/g, '')\n .toLowerCase()\n}\n\nexport async function moduleHasExport(filePath: string, exportName: string): Promise<boolean> {\n try {\n // On Windows, absolute paths must be file:// URLs for ESM imports\n // But package imports (starting with @ or not starting with . or /) should be used as-is\n const isAbsolutePath = path.isAbsolute(filePath)\n const importUrl = isAbsolutePath ? pathToFileURL(filePath).href : filePath\n const mod = await import(importUrl)\n return mod != null && Object.prototype.hasOwnProperty.call(mod, exportName)\n } catch {\n return false\n }\n}\n\nexport function logGenerationResult(label: string, changed: boolean): void {\n if (changed) {\n console.log(`Generated ${label}`)\n }\n}\n\nexport function createGeneratorResult(): GeneratorResult {\n return {\n filesWritten: [],\n filesUnchanged: [],\n errors: [],\n }\n}\n\nexport function writeGeneratedFile(options: {\n outFile: string\n checksumFile: string\n content: string\n structureChecksum: string\n result: GeneratorResult\n quiet?: boolean\n}): void {\n const { outFile, checksumFile, content, structureChecksum, result, quiet } = options\n const checksum = { content: calculateChecksum(content), structure: structureChecksum }\n const existing = readChecksumRecord(checksumFile)\n const shouldWrite =\n !existing ||\n existing.content !== checksum.content ||\n existing.structure !== checksum.structure\n if (shouldWrite) {\n fs.mkdirSync(path.dirname(outFile), { recursive: true })\n fs.writeFileSync(outFile, content)\n writeChecksumRecord(checksumFile, checksum)\n result.filesWritten.push(outFile)\n } else {\n result.filesUnchanged.push(outFile)\n }\n if (!quiet) logGenerationResult(path.relative(process.cwd(), outFile), shouldWrite)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import fs from 'node:fs'\nimport path from 'node:path'\nimport crypto from 'node:crypto'\nimport { pathToFileURL } from 'node:url'\n\nexport type ChecksumRecord = {\n content: string\n structure: string\n}\n\nexport interface GeneratorResult {\n filesWritten: string[]\n filesUnchanged: string[]\n errors: string[]\n}\n\nexport function calculateChecksum(content: string): string {\n return crypto.createHash('md5').update(content).digest('hex')\n}\n\nexport function readChecksumRecord(filePath: string): ChecksumRecord | null {\n if (!fs.existsSync(filePath)) {\n return null\n }\n try {\n const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8')) as Partial<ChecksumRecord>\n if (parsed && typeof parsed.content === 'string' && typeof parsed.structure === 'string') {\n return { content: parsed.content, structure: parsed.structure }\n }\n } catch {\n // Invalid checksum file\n }\n return null\n}\n\nexport function writeChecksumRecord(filePath: string, record: ChecksumRecord): void {\n fs.writeFileSync(filePath, JSON.stringify(record) + '\\n')\n}\n\nfunction collectStructureEntries(target: string, base: string, acc: string[]): void {\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(target, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))\n } catch (err) {\n acc.push(`error:${path.relative(base, target)}:${(err as Error).message}`)\n return\n }\n\n for (const entry of entries) {\n const fullPath = path.join(target, entry.name)\n const rel = path.relative(base, fullPath)\n try {\n const stat = fs.statSync(fullPath)\n if (entry.isDirectory()) {\n acc.push(`dir:${rel}:${stat.mtimeMs}`)\n collectStructureEntries(fullPath, base, acc)\n } else if (entry.isFile()) {\n acc.push(`file:${rel}:${stat.size}:${stat.mtimeMs}`)\n } else {\n acc.push(`other:${rel}:${stat.mtimeMs}`)\n }\n } catch {\n // File was deleted between readdir and stat - skip it\n continue\n }\n }\n}\n\nexport function calculateStructureChecksum(paths: string[]): string {\n const normalized = Array.from(new Set(paths.map((p) => path.resolve(p)))).sort()\n const entries: string[] = []\n for (const target of normalized) {\n if (!fs.existsSync(target)) {\n entries.push(`missing:${target}`)\n continue\n }\n const stat = fs.statSync(target)\n entries.push(`${stat.isDirectory() ? 'dir' : 'file'}:${target}:${stat.mtimeMs}`)\n if (stat.isDirectory()) collectStructureEntries(target, target, entries)\n }\n return calculateChecksum(entries.join('\\n'))\n}\n\nexport function writeIfChanged(\n filePath: string,\n content: string,\n checksumPath?: string,\n structureChecksum?: string\n): boolean {\n const newChecksum = calculateChecksum(content)\n\n if (checksumPath) {\n const existingRecord = readChecksumRecord(checksumPath)\n const newRecord: ChecksumRecord = {\n content: newChecksum,\n structure: structureChecksum || '',\n }\n\n const shouldWrite =\n !existingRecord ||\n existingRecord.content !== newRecord.content ||\n (structureChecksum && existingRecord.structure !== newRecord.structure)\n\n if (shouldWrite) {\n ensureDir(filePath)\n fs.writeFileSync(filePath, content)\n writeChecksumRecord(checksumPath, newRecord)\n return true\n }\n return false\n }\n\n // Simple comparison without checksum file\n if (fs.existsSync(filePath)) {\n const existing = fs.readFileSync(filePath, 'utf8')\n if (existing === content) {\n return false\n }\n }\n\n ensureDir(filePath)\n fs.writeFileSync(filePath, content)\n return true\n}\n\nexport function ensureDir(filePath: string): void {\n fs.mkdirSync(path.dirname(filePath), { recursive: true })\n}\n\n// Allowed path substrings for safe deletion. Include both POSIX and Windows separators.\nconst ALLOWED_RIMRAF_PATTERNS = [\n '/generated/',\n '/dist/',\n '/.mercato/',\n '/entities/',\n '\\\\generated\\\\',\n '\\\\dist\\\\',\n '\\\\.mercato\\\\',\n '\\\\entities\\\\',\n]\n\nexport function rimrafDir(dir: string, opts?: { allowedPatterns?: string[] }): void {\n if (!fs.existsSync(dir)) return\n\n // Safety check: only allow deletion within known safe directories\n const resolved = path.resolve(dir)\n const allowed = opts?.allowedPatterns ?? ALLOWED_RIMRAF_PATTERNS\n\n // Normalize resolved path to support matching against both POSIX and Windows patterns\n const normalized = {\n posix: resolved.replace(/\\\\/g, '/'),\n win: resolved.replace(/\\//g, '\\\\'),\n }\n\n if (!allowed.some((pattern) => normalized.posix.includes(pattern) || normalized.win.includes(pattern))) {\n throw new Error(`Refusing to delete directory outside allowed paths: ${resolved}. Allowed patterns: ${allowed.join(', ')}`)\n }\n\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const p = path.join(dir, entry.name)\n if (entry.isDirectory()) rimrafDir(p, opts)\n else fs.unlinkSync(p)\n }\n fs.rmdirSync(dir)\n}\n\nexport function toVar(s: string): string {\n return s.replace(/[^a-zA-Z0-9_]/g, '_')\n}\n\nexport function toSnake(s: string): string {\n return s\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .replace(/\\W+/g, '_')\n .replace(/_{2,}/g, '_')\n .replace(/^_+|_+$/g, '')\n .toLowerCase()\n}\n\nexport async function moduleHasExport(filePath: string, exportName: string): Promise<boolean> {\n try {\n // On Windows, absolute paths must be file:// URLs for ESM imports\n // But package imports (starting with @ or not starting with . or /) should be used as-is\n const isAbsolutePath = path.isAbsolute(filePath)\n const importUrl = isAbsolutePath ? pathToFileURL(filePath).href : filePath\n const mod = await import(importUrl)\n return mod != null && Object.prototype.hasOwnProperty.call(mod, exportName)\n } catch {\n return false\n }\n}\n\nexport function logGenerationResult(label: string, changed: boolean): void {\n if (changed) {\n console.log(`Generated ${label}`)\n }\n}\n\nexport function createGeneratorResult(): GeneratorResult {\n return {\n filesWritten: [],\n filesUnchanged: [],\n errors: [],\n }\n}\n\nexport function writeGeneratedFile(options: {\n outFile: string\n checksumFile: string\n content: string\n structureChecksum: string\n result: GeneratorResult\n quiet?: boolean\n}): void {\n const { outFile, checksumFile, content, structureChecksum, result, quiet } = options\n const checksum = { content: calculateChecksum(content), structure: structureChecksum }\n const existing = readChecksumRecord(checksumFile)\n const shouldWrite =\n !existing ||\n existing.content !== checksum.content ||\n existing.structure !== checksum.structure\n if (shouldWrite) {\n fs.mkdirSync(path.dirname(outFile), { recursive: true })\n fs.writeFileSync(outFile, content)\n writeChecksumRecord(checksumFile, checksum)\n result.filesWritten.push(outFile)\n } else {\n result.filesUnchanged.push(outFile)\n }\n if (!quiet) logGenerationResult(path.relative(process.cwd(), outFile), shouldWrite)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/cli",
3
- "version": "0.4.6-develop-e321a4e2a1",
3
+ "version": "0.4.6-main-24e64eef39",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "exports": {
@@ -58,7 +58,7 @@
58
58
  "@mikro-orm/core": "^6.6.2",
59
59
  "@mikro-orm/migrations": "^6.6.2",
60
60
  "@mikro-orm/postgresql": "^6.6.2",
61
- "@open-mercato/shared": "0.4.6-develop-e321a4e2a1",
61
+ "@open-mercato/shared": "0.4.6-main-24e64eef39",
62
62
  "pg": "8.16.3",
63
63
  "testcontainers": "^11.12.0",
64
64
  "typescript": "^5.9.3"
@@ -129,7 +129,7 @@ function writePerEntityFieldFiles(outRoot: string, fieldsByEntity: EntityFieldMa
129
129
  }
130
130
 
131
131
  function writeEntityFieldsRegistry(generatedRoot: string, fieldsByEntity: EntityFieldMap): void {
132
- const entities = Object.keys(fieldsByEntity).sort((a, b) => a.localeCompare(b))
132
+ const entities = Object.keys(fieldsByEntity).sort()
133
133
 
134
134
  // Always write the file, even if empty, to prevent TypeScript import errors
135
135
  const imports = entities.length > 0
@@ -407,10 +407,6 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
407
407
  const transFieldsChecksumFile = path.join(outputDir, 'translations-fields.generated.checksum')
408
408
  const enrichersOutFile = path.join(outputDir, 'enrichers.generated.ts')
409
409
  const enrichersChecksumFile = path.join(outputDir, 'enrichers.generated.checksum')
410
- const interceptorsOutFile = path.join(outputDir, 'interceptors.generated.ts')
411
- const interceptorsChecksumFile = path.join(outputDir, 'interceptors.generated.checksum')
412
- const componentOverridesOutFile = path.join(outputDir, 'component-overrides.generated.ts')
413
- const componentOverridesChecksumFile = path.join(outputDir, 'component-overrides.generated.checksum')
414
410
  const inboxActionsOutFile = path.join(outputDir, 'inbox-actions.generated.ts')
415
411
  const inboxActionsChecksumFile = path.join(outputDir, 'inbox-actions.generated.checksum')
416
412
 
@@ -444,10 +440,6 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
444
440
  const transFieldsImports: string[] = []
445
441
  const enricherConfigs: string[] = []
446
442
  const enricherImports: string[] = []
447
- const interceptorConfigs: string[] = []
448
- const interceptorImports: string[] = []
449
- const componentOverrideConfigs: string[] = []
450
- const componentOverrideImports: string[] = []
451
443
  const inboxActionsConfigs: string[] = []
452
444
  const inboxActionsImports: string[] = []
453
445
 
@@ -660,26 +652,6 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
660
652
  configExpr: (n, id) => `{ moduleId: '${id}', enrichers: ((${n} as any).enrichers ?? (${n} as any).default ?? []) }`,
661
653
  })
662
654
 
663
- // 10c. API interceptors: api/interceptors.ts
664
- processStandaloneConfig({
665
- roots, imps, modId, importIdRef,
666
- relativePath: 'api/interceptors.ts',
667
- prefix: 'INTERCEPTORS',
668
- standaloneImports: interceptorImports,
669
- standaloneConfigs: interceptorConfigs,
670
- configExpr: (n, id) => `{ moduleId: '${id}', interceptors: ((${n} as any).interceptors ?? (${n} as any).default ?? []) }`,
671
- })
672
-
673
- // 10d. Component overrides: widgets/components.ts
674
- processStandaloneConfig({
675
- roots, imps, modId, importIdRef,
676
- relativePath: 'widgets/components.ts',
677
- prefix: 'COMPONENT_OVERRIDES',
678
- standaloneImports: componentOverrideImports,
679
- standaloneConfigs: componentOverrideConfigs,
680
- configExpr: (n, id) => `{ moduleId: '${id}', componentOverrides: ((${n} as any).componentOverrides ?? (${n} as any).default ?? []) }`,
681
- })
682
-
683
655
  // Translatable fields: translations.ts (also referenced in module declarations)
684
656
  let transFieldsImportName: string | null = null
685
657
  transFieldsImportName = processStandaloneConfig({
@@ -1283,35 +1255,6 @@ export function getRegisteredActionTypes(): string[] {
1283
1255
  `
1284
1256
  writeGeneratedFile({ outFile: inboxActionsOutFile, checksumFile: inboxActionsChecksumFile, content: inboxActionsOutput, structureChecksum, result, quiet })
1285
1257
 
1286
- const interceptorEntriesLiteral = interceptorConfigs.join(',\n ')
1287
- const interceptorImportSection = interceptorImports.join('\n')
1288
- const interceptorsOutput = `// AUTO-GENERATED by mercato generate registry
1289
- import type { ApiInterceptor } from '@open-mercato/shared/lib/crud/api-interceptor'
1290
- ${interceptorImportSection ? `\n${interceptorImportSection}\n` : '\n'}type InterceptorEntry = { moduleId: string; interceptors: ApiInterceptor[] }
1291
-
1292
- export const interceptorEntries: InterceptorEntry[] = [
1293
- ${interceptorEntriesLiteral ? ` ${interceptorEntriesLiteral}\n` : ''}]
1294
- `
1295
- writeGeneratedFile({ outFile: interceptorsOutFile, checksumFile: interceptorsChecksumFile, content: interceptorsOutput, structureChecksum, result, quiet })
1296
-
1297
- const componentOverrideEntriesLiteral = componentOverrideConfigs.join(',\n ')
1298
- const componentOverrideImportSection = componentOverrideImports.join('\n')
1299
- const componentOverridesOutput = `// AUTO-GENERATED by mercato generate registry
1300
- import type { ComponentOverride } from '@open-mercato/shared/modules/widgets/component-registry'
1301
- ${componentOverrideImportSection ? `\n${componentOverrideImportSection}\n` : '\n'}type ComponentOverrideEntry = { moduleId: string; componentOverrides: ComponentOverride[] }
1302
-
1303
- export const componentOverrideEntries: ComponentOverrideEntry[] = [
1304
- ${componentOverrideEntriesLiteral ? ` ${componentOverrideEntriesLiteral}\n` : ''}]
1305
- `
1306
- writeGeneratedFile({
1307
- outFile: componentOverridesOutFile,
1308
- checksumFile: componentOverridesChecksumFile,
1309
- content: componentOverridesOutput,
1310
- structureChecksum,
1311
- result,
1312
- quiet,
1313
- })
1314
-
1315
1258
  return result
1316
1259
  }
1317
1260
 
@@ -160,7 +160,7 @@ const PLAYWRIGHT_ENV_UNAVAILABLE_PATTERNS: RegExp[] = [
160
160
  const PLAYWRIGHT_QUICK_FAILURE_THRESHOLD = 6
161
161
  const PLAYWRIGHT_QUICK_FAILURE_MAX_DURATION_MS = 1_500
162
162
  const PLAYWRIGHT_HEALTH_PROBE_INTERVAL_MS = 3_000
163
- const ANSI_ESCAPE_REGEX = /\x1b\[[0-?]*[ -/]*[@-~]/g // NOSONAR — ANSI escape sequence pattern
163
+ const ANSI_ESCAPE_REGEX = /\u001b\[[0-?]*[ -/]*[@-~]/g
164
164
  const NEXT_STATIC_ASSET_PATTERN = /\/_next\/static\/[^"'`\s)]+?\.(?:js|css)/g
165
165
  const resolver = createResolver()
166
166
  const projectRootDirectory = resolver.getRootDir()
@@ -755,7 +755,7 @@ async function buildSourceFingerprint(options: BuildCacheOptions = {}): Promise<
755
755
  }
756
756
 
757
757
  const fingerprintParts: string[] = []
758
- for (const filePath of absoluteFiles.sort((a, b) => a.localeCompare(b))) {
758
+ for (const filePath of absoluteFiles.sort()) {
759
759
  const fileStat = await stat(filePath)
760
760
  if (!fileStat.isFile()) {
761
761
  continue
package/src/lib/utils.ts CHANGED
@@ -67,7 +67,7 @@ function collectStructureEntries(target: string, base: string, acc: string[]): v
67
67
  }
68
68
 
69
69
  export function calculateStructureChecksum(paths: string[]): string {
70
- const normalized = Array.from(new Set(paths.map((p) => path.resolve(p)))).sort((a, b) => a.localeCompare(b))
70
+ const normalized = Array.from(new Set(paths.map((p) => path.resolve(p)))).sort()
71
71
  const entries: string[] = []
72
72
  for (const target of normalized) {
73
73
  if (!fs.existsSync(target)) {
@@ -173,7 +173,7 @@ export function toSnake(s: string): string {
173
173
  .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
174
174
  .replace(/\W+/g, '_')
175
175
  .replace(/_{2,}/g, '_')
176
- .replace(/(?:^_+|_+$)/g, '')
176
+ .replace(/^_+|_+$/g, '')
177
177
  .toLowerCase()
178
178
  }
179
179