@intlayer/engine 9.3.0 → 9.3.1
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/README.md +24 -24
- package/dist/assets/initConfig/templates/cjs.txt +1 -1
- package/dist/assets/initConfig/templates/mjs.txt +1 -1
- package/dist/assets/initConfig/templates/ts.txt +1 -1
- package/dist/cjs/_virtual/_rolldown/runtime.cjs +1 -2
- package/dist/cjs/_virtual/_utils_asset.cjs +2 -1
- package/dist/cjs/buildIntlayerDictionary/buildIntlayerDictionary.cjs.map +1 -1
- package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs +2 -1
- package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs.map +1 -1
- package/dist/cjs/buildIntlayerDictionary/writeFetchDictionary.cjs.map +1 -1
- package/dist/cjs/buildIntlayerDictionary/writeMergedDictionary.cjs +2 -1
- package/dist/cjs/buildIntlayerDictionary/writeMergedDictionary.cjs.map +1 -1
- package/dist/cjs/buildIntlayerDictionary/writeNestedDictionary.cjs.map +1 -1
- package/dist/cjs/buildIntlayerDictionary/writeRemoteDictionary.cjs +4 -2
- package/dist/cjs/buildIntlayerDictionary/writeRemoteDictionary.cjs.map +1 -1
- package/dist/cjs/buildIntlayerDictionary/writeUnmergedDictionary.cjs.map +1 -1
- package/dist/cjs/cleanOutputDir.cjs.map +1 -1
- package/dist/cjs/cleanRemovedContentDeclaration.cjs +7 -4
- package/dist/cjs/cleanRemovedContentDeclaration.cjs.map +1 -1
- package/dist/cjs/createDictionaryEntryPoint/createDictionaryEntryPoint.cjs +5 -3
- package/dist/cjs/createDictionaryEntryPoint/createDictionaryEntryPoint.cjs.map +1 -1
- package/dist/cjs/createDictionaryEntryPoint/generateDictionaryListContent.cjs.map +1 -1
- package/dist/cjs/createDictionaryEntryPoint/getBuiltDictionariesPath.cjs.map +1 -1
- package/dist/cjs/createDictionaryEntryPoint/getBuiltDynamicDictionariesPath.cjs.map +1 -1
- package/dist/cjs/createDictionaryEntryPoint/getBuiltFetchDictionariesPath.cjs.map +1 -1
- package/dist/cjs/createDictionaryEntryPoint/getBuiltRemoteDictionariesPath.cjs.map +1 -1
- package/dist/cjs/createDictionaryEntryPoint/getBuiltUnmergedDictionariesPath.cjs.map +1 -1
- package/dist/cjs/createType/createModuleAugmentation.cjs +2 -1
- package/dist/cjs/createType/createModuleAugmentation.cjs.map +1 -1
- package/dist/cjs/createType/createType.cjs.map +1 -1
- package/dist/cjs/detectFormatCommand.cjs.map +1 -1
- package/dist/cjs/docReview/alignBlocks.cjs +1 -1
- package/dist/cjs/docReview/alignBlocks.cjs.map +1 -1
- package/dist/cjs/docReview/computeSimilarity.cjs +0 -1
- package/dist/cjs/docReview/computeSimilarity.cjs.map +1 -1
- package/dist/cjs/docReview/mapChangedLinesToBlocks.cjs +0 -1
- package/dist/cjs/docReview/mapChangedLinesToBlocks.cjs.map +1 -1
- package/dist/cjs/docReview/normalizeBlock.cjs +0 -1
- package/dist/cjs/docReview/normalizeBlock.cjs.map +1 -1
- package/dist/cjs/docReview/planActions.cjs +0 -1
- package/dist/cjs/docReview/planActions.cjs.map +1 -1
- package/dist/cjs/docReview/rebuildDocument.cjs +0 -1
- package/dist/cjs/docReview/rebuildDocument.cjs.map +1 -1
- package/dist/cjs/docReview/reviewReport.cjs +2 -1
- package/dist/cjs/docReview/reviewReport.cjs.map +1 -1
- package/dist/cjs/docReview/segmentDocument.cjs +0 -1
- package/dist/cjs/docReview/segmentDocument.cjs.map +1 -1
- package/dist/cjs/fetchDistantDictionaries.cjs.map +1 -1
- package/dist/cjs/filterInvalidDictionaries.cjs.map +1 -1
- package/dist/cjs/formatDictionary.cjs.map +1 -1
- package/dist/cjs/getContentDeclarationFileTemplate/getContentDeclarationFileTemplate.cjs.map +1 -1
- package/dist/cjs/handleAdditionalContentDeclarationFile.cjs +4 -2
- package/dist/cjs/handleAdditionalContentDeclarationFile.cjs.map +1 -1
- package/dist/cjs/handleContentDeclarationFileChange.cjs +6 -2
- package/dist/cjs/handleContentDeclarationFileChange.cjs.map +1 -1
- package/dist/cjs/handleContentDeclarationFileMoved.cjs.map +1 -1
- package/dist/cjs/handleUnlinkedContentDeclarationFile.cjs +4 -2
- package/dist/cjs/handleUnlinkedContentDeclarationFile.cjs.map +1 -1
- package/dist/cjs/init/cms.cjs.map +1 -1
- package/dist/cjs/init/frameworkSetup/nextAppRouter/detect.cjs.map +1 -1
- package/dist/cjs/init/frameworkSetup/nextAppRouter/index.cjs +1 -3
- package/dist/cjs/init/frameworkSetup/nextAppRouter/index.cjs.map +1 -1
- package/dist/cjs/init/frameworkSetup/nextAppRouter/restructure.cjs +2 -1
- package/dist/cjs/init/frameworkSetup/nextAppRouter/restructure.cjs.map +1 -1
- package/dist/cjs/init/frameworkSetup/nextAppRouter/templates.cjs +0 -1
- package/dist/cjs/init/frameworkSetup/nextAppRouter/templates.cjs.map +1 -1
- package/dist/cjs/init/frameworkSetup/tanstackStart/detect.cjs.map +1 -1
- package/dist/cjs/init/frameworkSetup/tanstackStart/index.cjs +3 -5
- package/dist/cjs/init/frameworkSetup/tanstackStart/index.cjs.map +1 -1
- package/dist/cjs/init/frameworkSetup/tanstackStart/restructure.cjs +2 -1
- package/dist/cjs/init/frameworkSetup/tanstackStart/restructure.cjs.map +1 -1
- package/dist/cjs/init/frameworkSetup/tanstackStart/templates.cjs +18 -12
- package/dist/cjs/init/frameworkSetup/tanstackStart/templates.cjs.map +1 -1
- package/dist/cjs/init/frameworkSetup/tanstackStart/transforms.cjs +57 -14
- package/dist/cjs/init/frameworkSetup/tanstackStart/transforms.cjs.map +1 -1
- package/dist/cjs/init/index.cjs +66 -38
- package/dist/cjs/init/index.cjs.map +1 -1
- package/dist/cjs/init/utils/configManipulation.cjs.map +1 -1
- package/dist/cjs/init/utils/fileSystem.cjs +3 -1
- package/dist/cjs/init/utils/fileSystem.cjs.map +1 -1
- package/dist/cjs/init/utils/githubActions.cjs.map +1 -1
- package/dist/cjs/init/utils/jsonParser.cjs +0 -1
- package/dist/cjs/init/utils/jsonParser.cjs.map +1 -1
- package/dist/cjs/init/utils/nextCompilerBabel.cjs.map +1 -1
- package/dist/cjs/init/utils/packageManager.cjs +4 -2
- package/dist/cjs/init/utils/packageManager.cjs.map +1 -1
- package/dist/cjs/init/utils/tsConfig.cjs.map +1 -1
- package/dist/cjs/init/utils/version.cjs +0 -1
- package/dist/cjs/init/utils/version.cjs.map +1 -1
- package/dist/cjs/initConfig/index.cjs +12 -8
- package/dist/cjs/initConfig/index.cjs.map +1 -1
- package/dist/cjs/installSkills/index.cjs.map +1 -1
- package/dist/cjs/listDictionariesPath.cjs +6 -5
- package/dist/cjs/listDictionariesPath.cjs.map +1 -1
- package/dist/cjs/listGitFiles.cjs.map +1 -1
- package/dist/cjs/listProjects.cjs +2 -1
- package/dist/cjs/listProjects.cjs.map +1 -1
- package/dist/cjs/loadDictionaries/getIntlayerBundle.cjs +2 -1
- package/dist/cjs/loadDictionaries/getIntlayerBundle.cjs.map +1 -1
- package/dist/cjs/loadDictionaries/loadContentDeclaration.cjs +8 -5
- package/dist/cjs/loadDictionaries/loadContentDeclaration.cjs.map +1 -1
- package/dist/cjs/loadDictionaries/loadDictionaries.cjs.map +1 -1
- package/dist/cjs/loadDictionaries/loadLocalDictionaries.cjs.map +1 -1
- package/dist/cjs/loadDictionaries/loadMarkdownContentDeclaration.cjs.map +1 -1
- package/dist/cjs/loadDictionaries/loadRemoteDictionaries.cjs.map +1 -1
- package/dist/cjs/loadDictionaries/loadYamlContentDeclaration.cjs +2 -1
- package/dist/cjs/loadDictionaries/loadYamlContentDeclaration.cjs.map +1 -1
- package/dist/cjs/loadDictionaries/log.cjs +10 -6
- package/dist/cjs/loadDictionaries/log.cjs.map +1 -1
- package/dist/cjs/loadDictionaries/logTypeScriptErrors.cjs.map +1 -1
- package/dist/cjs/logConfigDetails.cjs +4 -3
- package/dist/cjs/logConfigDetails.cjs.map +1 -1
- package/dist/cjs/prepareIntlayer.cjs +5 -3
- package/dist/cjs/prepareIntlayer.cjs.map +1 -1
- package/dist/cjs/reduceDictionaryContent/applyMask.cjs +0 -1
- package/dist/cjs/reduceDictionaryContent/applyMask.cjs.map +1 -1
- package/dist/cjs/reduceDictionaryContent/reduceDictionaryContent.cjs +2 -1
- package/dist/cjs/reduceDictionaryContent/reduceDictionaryContent.cjs.map +1 -1
- package/dist/cjs/scan/calculateScore.cjs +0 -1
- package/dist/cjs/scan/calculateScore.cjs.map +1 -1
- package/dist/cjs/scan/checks.cjs +1 -1
- package/dist/cjs/scan/checks.cjs.map +1 -1
- package/dist/cjs/scan/parseHtml.cjs +0 -1
- package/dist/cjs/scan/parseHtml.cjs.map +1 -1
- package/dist/cjs/scan/scanWebsite.cjs.map +1 -1
- package/dist/cjs/syncPluginKit/createFileAdapter.cjs +2 -1
- package/dist/cjs/syncPluginKit/createFileAdapter.cjs.map +1 -1
- package/dist/cjs/syncPluginKit/createSyncPlugin.cjs +4 -1
- package/dist/cjs/syncPluginKit/createSyncPlugin.cjs.map +1 -1
- package/dist/cjs/syncPluginKit/extractKeyAndLocaleFromPath.cjs +0 -1
- package/dist/cjs/syncPluginKit/extractKeyAndLocaleFromPath.cjs.map +1 -1
- package/dist/cjs/syncPluginKit/filePathPatternHelpers.cjs +0 -1
- package/dist/cjs/syncPluginKit/filePathPatternHelpers.cjs.map +1 -1
- package/dist/cjs/utils/autoDecorateContent.cjs.map +1 -1
- package/dist/cjs/utils/babelParser.cjs.map +1 -1
- package/dist/cjs/utils/buildComponentFilesList.cjs.map +1 -1
- package/dist/cjs/utils/buildFilesList.cjs.map +1 -1
- package/dist/cjs/utils/chunkArray.cjs +0 -1
- package/dist/cjs/utils/chunkArray.cjs.map +1 -1
- package/dist/cjs/utils/formatter.cjs.map +1 -1
- package/dist/cjs/utils/getContentExtension.cjs +1 -4
- package/dist/cjs/utils/getContentExtension.cjs.map +1 -1
- package/dist/cjs/utils/getFormatFromExtension.cjs +0 -1
- package/dist/cjs/utils/getFormatFromExtension.cjs.map +1 -1
- package/dist/cjs/utils/getPathHash.cjs +0 -1
- package/dist/cjs/utils/getPathHash.cjs.map +1 -1
- package/dist/cjs/utils/mergeChunks.cjs +0 -1
- package/dist/cjs/utils/mergeChunks.cjs.map +1 -1
- package/dist/cjs/utils/pLimit.cjs +0 -1
- package/dist/cjs/utils/pLimit.cjs.map +1 -1
- package/dist/cjs/utils/readDictionariesFromDisk.cjs.map +1 -1
- package/dist/cjs/utils/reduceObjectFormat.cjs +0 -1
- package/dist/cjs/utils/reduceObjectFormat.cjs.map +1 -1
- package/dist/cjs/utils/resolveObjectPromises.cjs +0 -1
- package/dist/cjs/utils/resolveObjectPromises.cjs.map +1 -1
- package/dist/cjs/utils/resolveRelativePath.cjs +4 -1
- package/dist/cjs/utils/resolveRelativePath.cjs.map +1 -1
- package/dist/cjs/utils/runOnce.cjs +2 -2
- package/dist/cjs/utils/runOnce.cjs.map +1 -1
- package/dist/cjs/utils/runParallel/bin.cjs.map +1 -1
- package/dist/cjs/utils/runParallel/bootstrap.cjs +0 -1
- package/dist/cjs/utils/runParallel/bootstrap.cjs.map +1 -1
- package/dist/cjs/utils/runParallel/index.cjs.map +1 -1
- package/dist/cjs/utils/runParallel/spawnPosix.cjs.map +1 -1
- package/dist/cjs/utils/runParallel/spawnWin32.cjs.map +1 -1
- package/dist/cjs/utils/sortAlphabetically.cjs +0 -1
- package/dist/cjs/utils/sortAlphabetically.cjs.map +1 -1
- package/dist/cjs/utils/splitTextByLine.cjs +0 -1
- package/dist/cjs/utils/splitTextByLine.cjs.map +1 -1
- package/dist/cjs/utils/verifyIdenticObjectFormat.cjs +0 -1
- package/dist/cjs/utils/verifyIdenticObjectFormat.cjs.map +1 -1
- package/dist/cjs/watcher.cjs +4 -2
- package/dist/cjs/watcher.cjs.map +1 -1
- package/dist/cjs/writeConfiguration/index.cjs +2 -1
- package/dist/cjs/writeConfiguration/index.cjs.map +1 -1
- package/dist/cjs/writeContentDeclaration/detectExportedComponentName.cjs +0 -1
- package/dist/cjs/writeContentDeclaration/detectExportedComponentName.cjs.map +1 -1
- package/dist/cjs/writeContentDeclaration/processContentDeclarationContent.cjs.map +1 -1
- package/dist/cjs/writeContentDeclaration/transformJSFile.cjs +26 -23
- package/dist/cjs/writeContentDeclaration/transformJSFile.cjs.map +1 -1
- package/dist/cjs/writeContentDeclaration/transformJSONFile.cjs +6 -5
- package/dist/cjs/writeContentDeclaration/transformJSONFile.cjs.map +1 -1
- package/dist/cjs/writeContentDeclaration/writeContentDeclaration.cjs +9 -3
- package/dist/cjs/writeContentDeclaration/writeContentDeclaration.cjs.map +1 -1
- package/dist/cjs/writeContentDeclaration/writeJSFile.cjs +2 -1
- package/dist/cjs/writeContentDeclaration/writeJSFile.cjs.map +1 -1
- package/dist/cjs/writeContentDeclaration/writeMarkdownFile.cjs +2 -1
- package/dist/cjs/writeContentDeclaration/writeMarkdownFile.cjs.map +1 -1
- package/dist/cjs/writeContentDeclaration/writeYamlFile.cjs +4 -2
- package/dist/cjs/writeContentDeclaration/writeYamlFile.cjs.map +1 -1
- package/dist/cjs/writeFileIfChanged.cjs.map +1 -1
- package/dist/esm/_virtual/_utils_asset.mjs +2 -1
- package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs +2 -1
- package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs.map +1 -1
- package/dist/esm/buildIntlayerDictionary/writeMergedDictionary.mjs +2 -1
- package/dist/esm/buildIntlayerDictionary/writeMergedDictionary.mjs.map +1 -1
- package/dist/esm/buildIntlayerDictionary/writeRemoteDictionary.mjs +4 -2
- package/dist/esm/buildIntlayerDictionary/writeRemoteDictionary.mjs.map +1 -1
- package/dist/esm/cleanRemovedContentDeclaration.mjs +7 -4
- package/dist/esm/cleanRemovedContentDeclaration.mjs.map +1 -1
- package/dist/esm/createDictionaryEntryPoint/createDictionaryEntryPoint.mjs +5 -3
- package/dist/esm/createDictionaryEntryPoint/createDictionaryEntryPoint.mjs.map +1 -1
- package/dist/esm/createType/createModuleAugmentation.mjs +2 -1
- package/dist/esm/createType/createModuleAugmentation.mjs.map +1 -1
- package/dist/esm/docReview/alignBlocks.mjs +1 -1
- package/dist/esm/docReview/alignBlocks.mjs.map +1 -1
- package/dist/esm/docReview/reviewReport.mjs +2 -1
- package/dist/esm/docReview/reviewReport.mjs.map +1 -1
- package/dist/esm/handleAdditionalContentDeclarationFile.mjs +4 -2
- package/dist/esm/handleAdditionalContentDeclarationFile.mjs.map +1 -1
- package/dist/esm/handleContentDeclarationFileChange.mjs +6 -2
- package/dist/esm/handleContentDeclarationFileChange.mjs.map +1 -1
- package/dist/esm/handleUnlinkedContentDeclarationFile.mjs +4 -2
- package/dist/esm/handleUnlinkedContentDeclarationFile.mjs.map +1 -1
- package/dist/esm/init/frameworkSetup/nextAppRouter/index.mjs +1 -3
- package/dist/esm/init/frameworkSetup/nextAppRouter/index.mjs.map +1 -1
- package/dist/esm/init/frameworkSetup/nextAppRouter/restructure.mjs +2 -1
- package/dist/esm/init/frameworkSetup/nextAppRouter/restructure.mjs.map +1 -1
- package/dist/esm/init/frameworkSetup/tanstackStart/index.mjs +4 -6
- package/dist/esm/init/frameworkSetup/tanstackStart/index.mjs.map +1 -1
- package/dist/esm/init/frameworkSetup/tanstackStart/restructure.mjs +2 -1
- package/dist/esm/init/frameworkSetup/tanstackStart/restructure.mjs.map +1 -1
- package/dist/esm/init/frameworkSetup/tanstackStart/templates.mjs +17 -10
- package/dist/esm/init/frameworkSetup/tanstackStart/templates.mjs.map +1 -1
- package/dist/esm/init/frameworkSetup/tanstackStart/transforms.mjs +58 -15
- package/dist/esm/init/frameworkSetup/tanstackStart/transforms.mjs.map +1 -1
- package/dist/esm/init/index.mjs +66 -38
- package/dist/esm/init/index.mjs.map +1 -1
- package/dist/esm/init/utils/configManipulation.mjs.map +1 -1
- package/dist/esm/init/utils/fileSystem.mjs +3 -1
- package/dist/esm/init/utils/fileSystem.mjs.map +1 -1
- package/dist/esm/init/utils/packageManager.mjs +4 -2
- package/dist/esm/init/utils/packageManager.mjs.map +1 -1
- package/dist/esm/initConfig/index.mjs +12 -8
- package/dist/esm/initConfig/index.mjs.map +1 -1
- package/dist/esm/listDictionariesPath.mjs +6 -5
- package/dist/esm/listDictionariesPath.mjs.map +1 -1
- package/dist/esm/listProjects.mjs +2 -1
- package/dist/esm/listProjects.mjs.map +1 -1
- package/dist/esm/loadDictionaries/getIntlayerBundle.mjs +2 -1
- package/dist/esm/loadDictionaries/getIntlayerBundle.mjs.map +1 -1
- package/dist/esm/loadDictionaries/loadContentDeclaration.mjs +8 -5
- package/dist/esm/loadDictionaries/loadContentDeclaration.mjs.map +1 -1
- package/dist/esm/loadDictionaries/loadYamlContentDeclaration.mjs +2 -1
- package/dist/esm/loadDictionaries/loadYamlContentDeclaration.mjs.map +1 -1
- package/dist/esm/loadDictionaries/log.mjs +10 -6
- package/dist/esm/loadDictionaries/log.mjs.map +1 -1
- package/dist/esm/logConfigDetails.mjs +4 -3
- package/dist/esm/logConfigDetails.mjs.map +1 -1
- package/dist/esm/prepareIntlayer.mjs +5 -3
- package/dist/esm/prepareIntlayer.mjs.map +1 -1
- package/dist/esm/reduceDictionaryContent/reduceDictionaryContent.mjs +2 -1
- package/dist/esm/reduceDictionaryContent/reduceDictionaryContent.mjs.map +1 -1
- package/dist/esm/scan/checks.mjs +1 -1
- package/dist/esm/scan/checks.mjs.map +1 -1
- package/dist/esm/syncPluginKit/createFileAdapter.mjs +2 -1
- package/dist/esm/syncPluginKit/createFileAdapter.mjs.map +1 -1
- package/dist/esm/syncPluginKit/createSyncPlugin.mjs +4 -1
- package/dist/esm/syncPluginKit/createSyncPlugin.mjs.map +1 -1
- package/dist/esm/utils/babelParser.mjs.map +1 -1
- package/dist/esm/utils/getContentExtension.mjs +1 -3
- package/dist/esm/utils/getContentExtension.mjs.map +1 -1
- package/dist/esm/utils/resolveRelativePath.mjs +4 -1
- package/dist/esm/utils/resolveRelativePath.mjs.map +1 -1
- package/dist/esm/utils/runOnce.mjs +2 -2
- package/dist/esm/utils/runOnce.mjs.map +1 -1
- package/dist/esm/watcher.mjs +4 -2
- package/dist/esm/watcher.mjs.map +1 -1
- package/dist/esm/writeConfiguration/index.mjs +2 -1
- package/dist/esm/writeConfiguration/index.mjs.map +1 -1
- package/dist/esm/writeContentDeclaration/transformJSFile.mjs +26 -23
- package/dist/esm/writeContentDeclaration/transformJSFile.mjs.map +1 -1
- package/dist/esm/writeContentDeclaration/transformJSONFile.mjs +6 -5
- package/dist/esm/writeContentDeclaration/transformJSONFile.mjs.map +1 -1
- package/dist/esm/writeContentDeclaration/writeContentDeclaration.mjs +9 -3
- package/dist/esm/writeContentDeclaration/writeContentDeclaration.mjs.map +1 -1
- package/dist/esm/writeContentDeclaration/writeJSFile.mjs +2 -1
- package/dist/esm/writeContentDeclaration/writeJSFile.mjs.map +1 -1
- package/dist/esm/writeContentDeclaration/writeMarkdownFile.mjs +2 -1
- package/dist/esm/writeContentDeclaration/writeMarkdownFile.mjs.map +1 -1
- package/dist/esm/writeContentDeclaration/writeYamlFile.mjs +4 -2
- package/dist/esm/writeContentDeclaration/writeYamlFile.mjs.map +1 -1
- package/dist/types/init/frameworkSetup/tanstackStart/templates.d.ts +9 -4
- package/dist/types/init/frameworkSetup/tanstackStart/templates.d.ts.map +1 -1
- package/dist/types/init/frameworkSetup/tanstackStart/transforms.d.ts +6 -2
- package/dist/types/init/frameworkSetup/tanstackStart/transforms.d.ts.map +1 -1
- package/package.json +10 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log.mjs","names":[],"sources":["../../../src/loadDictionaries/log.ts"],"sourcesContent":["import { log } from '@intlayer/config/built';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colorize,\n getPrefix,\n spinnerFrames,\n v,\n x,\n} from '@intlayer/config/logger';\nimport { extractErrorMessage } from '@intlayer/config/utils';\nimport type { DictionariesStatus } from './loadDictionaries';\n\nexport class DictionariesLogger {\n private statuses: DictionariesStatus[] = [];\n private spinnerTimer: NodeJS.Timeout | null = null;\n private spinnerIndex = 0;\n private renderedLines = 0;\n private readonly spinnerFrames = spinnerFrames;\n private isFinished = false;\n private readonly prefix: string;\n private lastRenderedState: string = '';\n private remoteCheckInProgress = false;\n private expectRemote = false;\n private remoteError: string | undefined;\n private pluginTotal = 0;\n private pluginDone = 0;\n private pluginNames: string[] = [];\n private pluginError: string | undefined;\n\n constructor() {\n this.prefix = getPrefix(log?.prefix) ?? '';\n }\n\n setExpectRemote(expect: boolean) {\n this.expectRemote = expect;\n }\n\n startRemoteCheck() {\n if (this.isFinished) return;\n this.remoteCheckInProgress = true;\n this.startSpinner();\n this.render();\n }\n\n stopRemoteCheck() {\n this.remoteCheckInProgress = false;\n }\n\n update(newStatuses: DictionariesStatus[]) {\n if (this.isFinished) return;\n for (const status of newStatuses) {\n const index = this.statuses.findIndex(\n (s) =>\n s.dictionaryKey === status.dictionaryKey && s.type === status.type\n );\n if (index >= 0) {\n this.statuses[index] = status;\n } else {\n this.statuses.push(status);\n }\n }\n\n // If we expect remote fetch later, avoid rendering a local-only line first\n const { remoteTotal } = this.computeProgress();\n if (this.expectRemote && !this.remoteCheckInProgress && remoteTotal === 0) {\n // Do not start spinner or render yet; wait until remote check starts\n return;\n }\n\n this.startSpinner();\n this.render();\n }\n\n finish() {\n this.isFinished = true;\n this.stopSpinner();\n // Render final state and keep it visible\n this.render();\n }\n\n private startSpinner() {\n if (this.spinnerTimer || this.isFinished) return;\n this.spinnerTimer = setInterval(() => {\n this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length;\n this.render();\n }, 100);\n }\n\n private stopSpinner() {\n if (!this.spinnerTimer) return;\n clearInterval(this.spinnerTimer);\n this.spinnerTimer = null;\n }\n\n public setRemoteError = (error?: Error) => {\n this.remoteError = extractErrorMessage(error);\n // Avoid rendering a transient remote-only line while the remote check flag is still true\n // Ensure local + remote are rendered together after a failure\n this.stopRemoteCheck();\n this.render();\n };\n\n /**\n * Register the names of the plugins providing dictionaries.\n * They are displayed next to the plugin progress counters, e.g.\n * `Plugin content: 6/6 (sync-json, load-po)`.\n *\n * @param names - Names of the plugins exposing a `loadDictionaries` hook\n */\n setPluginNames(names: string[]) {\n if (this.isFinished) return;\n // Guard against plugins declared without a name at runtime\n this.pluginNames = names.filter((name) => Boolean(name));\n this.render();\n }\n\n setPluginTotal(total: number) {\n if (this.isFinished) return;\n this.pluginTotal = total;\n if (total > 0) {\n this.startSpinner();\n }\n this.render();\n }\n\n setPluginDone(done: number) {\n if (this.isFinished) return;\n this.pluginDone = done;\n this.render();\n }\n\n /**\n * Flag the plugin line as errored.\n *\n * @param error - Error thrown while loading the plugin dictionaries\n * @param pluginName - Name of the plugin that threw, prefixed to the message\n */\n setPluginError(error?: Error, pluginName?: string) {\n if (this.isFinished) return;\n const errorMessage = extractErrorMessage(error);\n this.pluginError = pluginName\n ? `${pluginName}: ${errorMessage}`\n : errorMessage;\n this.render();\n }\n\n private render() {\n const {\n localTotal,\n localDone,\n remoteTotal,\n remoteDone,\n pluginTotal,\n pluginDone,\n } = this.computeProgress();\n\n const frame = this.spinnerFrames[this.spinnerIndex];\n const clock = colorize(frame, ANSIColors.BLUE);\n const lines: string[] = [];\n\n const isLocalDone = localDone === localTotal;\n const isRemoteDone = remoteDone === remoteTotal;\n const isPluginDone = pluginDone === pluginTotal;\n\n const suppressLocalWhileCheckingRemote =\n this.expectRemote && this.remoteCheckInProgress && remoteTotal === 0;\n\n if (!suppressLocalWhileCheckingRemote) {\n if (isLocalDone) {\n lines.push(\n `${this.prefix} ${v} Local content: ${colorize(`${localDone}`, ANSIColors.GREEN)}${colorize(`/${localTotal}`, ANSIColors.GREY)}`\n );\n } else {\n lines.push(\n `${this.prefix} ${clock} Local content: ${colorize(`${localDone}`, ANSIColors.BLUE)}${colorize(`/${localTotal}`, ANSIColors.GREY)}`\n );\n }\n }\n\n // Single remote line: show error, check, or progress counts\n if (remoteTotal > 0 || this.remoteCheckInProgress || this.remoteError) {\n if (this.remoteError) {\n lines.push(\n `${this.prefix} ${x} Remote content: ${colorize(\n this.remoteError,\n ANSIColors.RED\n )}`\n );\n } else if (remoteTotal === 0) {\n lines.push(\n `${this.prefix} ${clock} Remote content: ${colorize('Check server', ANSIColors.BLUE)}`\n );\n } else if (isRemoteDone) {\n lines.push(\n `${this.prefix} ${v} Remote content: ${colorize(`${remoteDone}`, ANSIColors.GREEN)}${colorize(`/${remoteTotal}`, ANSIColors.GREY)}`\n );\n } else {\n lines.push(\n `${this.prefix} ${clock} Remote content: ${colorize(`${remoteDone}`, ANSIColors.BLUE)}${colorize(`/${remoteTotal}`, ANSIColors.GREY)}`\n );\n }\n }\n\n // Plugin line: show error or progress counts, suffixed by the plugin names\n if (pluginTotal > 0 || this.pluginError) {\n const pluginNamesSuffix =\n this.pluginNames.length > 0\n ? colorize(` (${this.pluginNames.join(', ')})`, ANSIColors.GREY)\n : '';\n\n if (this.pluginError) {\n lines.push(\n `${this.prefix} ${x} Plugin content: ${colorize(\n this.pluginError,\n ANSIColors.RED\n )}`\n );\n } else if (isPluginDone) {\n lines.push(\n `${this.prefix} ${v} Plugin content: ${colorize(`${pluginDone}`, ANSIColors.GREEN)}${colorize(`/${pluginTotal}`, ANSIColors.GREY)}${pluginNamesSuffix}`\n );\n } else {\n lines.push(\n `${this.prefix} ${clock} Plugin content: ${colorize(`${pluginDone}`, ANSIColors.BLUE)}${colorize(`/${pluginTotal}`, ANSIColors.GREY)}${pluginNamesSuffix}`\n );\n }\n }\n\n // Check if the state has changed to avoid duplicate rendering\n const currentState = lines.join('\\n');\n if (currentState === this.lastRenderedState) {\n return;\n }\n this.lastRenderedState = currentState;\n\n if (this.renderedLines > 0) {\n process.stdout.write(`\\x1b[${this.renderedLines}F`);\n }\n\n const totalLinesToClear = Math.max(this.renderedLines, lines.length);\n for (let i = 0; i < totalLinesToClear; i++) {\n process.stdout.write('\\x1b[2K');\n const line = lines[i];\n if (line !== undefined) {\n process.stdout.write(line);\n }\n process.stdout.write('\\n');\n }\n\n this.renderedLines = lines.length;\n }\n\n private computeProgress() {\n const localKeys = new Set(\n this.statuses\n .filter((s) => s.type === 'local')\n .map((s) => s.dictionaryKey)\n );\n\n const localDoneKeys = new Set(\n this.statuses\n .filter(\n (s) =>\n s.type === 'local' && (s.status === 'built' || s.status === 'error')\n )\n .map((s) => s.dictionaryKey)\n );\n\n const remoteKeys = new Set(\n this.statuses\n .filter((s) => s.type === 'remote')\n .map((s) => s.dictionaryKey)\n );\n\n const remoteDoneKeys = new Set(\n this.statuses\n .filter(\n (s) =>\n s.type === 'remote' &&\n (s.status === 'fetched' ||\n s.status === 'imported' ||\n s.status === 'error')\n )\n .map((s) => s.dictionaryKey)\n );\n\n return {\n localTotal: localKeys.size,\n localDone: localDoneKeys.size,\n remoteTotal: remoteKeys.size,\n remoteDone: remoteDoneKeys.size,\n pluginTotal: this.pluginTotal,\n pluginDone: this.pluginDone,\n } as const;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"log.mjs","names":[],"sources":["../../../src/loadDictionaries/log.ts"],"sourcesContent":["import { log } from '@intlayer/config/built';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colorize,\n getPrefix,\n spinnerFrames,\n v,\n x,\n} from '@intlayer/config/logger';\nimport { extractErrorMessage } from '@intlayer/config/utils';\nimport type { DictionariesStatus } from './loadDictionaries';\n\nexport class DictionariesLogger {\n private statuses: DictionariesStatus[] = [];\n private spinnerTimer: NodeJS.Timeout | null = null;\n private spinnerIndex = 0;\n private renderedLines = 0;\n private readonly spinnerFrames = spinnerFrames;\n private isFinished = false;\n private readonly prefix: string;\n private lastRenderedState: string = '';\n private remoteCheckInProgress = false;\n private expectRemote = false;\n private remoteError: string | undefined;\n private pluginTotal = 0;\n private pluginDone = 0;\n private pluginNames: string[] = [];\n private pluginError: string | undefined;\n\n constructor() {\n this.prefix = getPrefix(log?.prefix) ?? '';\n }\n\n setExpectRemote(expect: boolean) {\n this.expectRemote = expect;\n }\n\n startRemoteCheck() {\n if (this.isFinished) return;\n this.remoteCheckInProgress = true;\n this.startSpinner();\n this.render();\n }\n\n stopRemoteCheck() {\n this.remoteCheckInProgress = false;\n }\n\n update(newStatuses: DictionariesStatus[]) {\n if (this.isFinished) return;\n for (const status of newStatuses) {\n const index = this.statuses.findIndex(\n (s) =>\n s.dictionaryKey === status.dictionaryKey && s.type === status.type\n );\n if (index >= 0) {\n this.statuses[index] = status;\n } else {\n this.statuses.push(status);\n }\n }\n\n // If we expect remote fetch later, avoid rendering a local-only line first\n const { remoteTotal } = this.computeProgress();\n if (this.expectRemote && !this.remoteCheckInProgress && remoteTotal === 0) {\n // Do not start spinner or render yet; wait until remote check starts\n return;\n }\n\n this.startSpinner();\n this.render();\n }\n\n finish() {\n this.isFinished = true;\n this.stopSpinner();\n // Render final state and keep it visible\n this.render();\n }\n\n private startSpinner() {\n if (this.spinnerTimer || this.isFinished) return;\n this.spinnerTimer = setInterval(() => {\n this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length;\n this.render();\n }, 100);\n }\n\n private stopSpinner() {\n if (!this.spinnerTimer) return;\n clearInterval(this.spinnerTimer);\n this.spinnerTimer = null;\n }\n\n public setRemoteError = (error?: Error) => {\n this.remoteError = extractErrorMessage(error);\n // Avoid rendering a transient remote-only line while the remote check flag is still true\n // Ensure local + remote are rendered together after a failure\n this.stopRemoteCheck();\n this.render();\n };\n\n /**\n * Register the names of the plugins providing dictionaries.\n * They are displayed next to the plugin progress counters, e.g.\n * `Plugin content: 6/6 (sync-json, load-po)`.\n *\n * @param names - Names of the plugins exposing a `loadDictionaries` hook\n */\n setPluginNames(names: string[]) {\n if (this.isFinished) return;\n // Guard against plugins declared without a name at runtime\n this.pluginNames = names.filter((name) => Boolean(name));\n this.render();\n }\n\n setPluginTotal(total: number) {\n if (this.isFinished) return;\n this.pluginTotal = total;\n if (total > 0) {\n this.startSpinner();\n }\n this.render();\n }\n\n setPluginDone(done: number) {\n if (this.isFinished) return;\n this.pluginDone = done;\n this.render();\n }\n\n /**\n * Flag the plugin line as errored.\n *\n * @param error - Error thrown while loading the plugin dictionaries\n * @param pluginName - Name of the plugin that threw, prefixed to the message\n */\n setPluginError(error?: Error, pluginName?: string) {\n if (this.isFinished) return;\n const errorMessage = extractErrorMessage(error);\n this.pluginError = pluginName\n ? `${pluginName}: ${errorMessage}`\n : errorMessage;\n this.render();\n }\n\n private render() {\n const {\n localTotal,\n localDone,\n remoteTotal,\n remoteDone,\n pluginTotal,\n pluginDone,\n } = this.computeProgress();\n\n const frame = this.spinnerFrames[this.spinnerIndex];\n const clock = colorize(frame, ANSIColors.BLUE);\n const lines: string[] = [];\n\n const isLocalDone = localDone === localTotal;\n const isRemoteDone = remoteDone === remoteTotal;\n const isPluginDone = pluginDone === pluginTotal;\n\n const suppressLocalWhileCheckingRemote =\n this.expectRemote && this.remoteCheckInProgress && remoteTotal === 0;\n\n if (!suppressLocalWhileCheckingRemote) {\n if (isLocalDone) {\n lines.push(\n `${this.prefix} ${v} Local content: ${colorize(`${localDone}`, ANSIColors.GREEN)}${colorize(`/${localTotal}`, ANSIColors.GREY)}`\n );\n } else {\n lines.push(\n `${this.prefix} ${clock} Local content: ${colorize(`${localDone}`, ANSIColors.BLUE)}${colorize(`/${localTotal}`, ANSIColors.GREY)}`\n );\n }\n }\n\n // Single remote line: show error, check, or progress counts\n if (remoteTotal > 0 || this.remoteCheckInProgress || this.remoteError) {\n if (this.remoteError) {\n lines.push(\n `${this.prefix} ${x} Remote content: ${colorize(\n this.remoteError,\n ANSIColors.RED\n )}`\n );\n } else if (remoteTotal === 0) {\n lines.push(\n `${this.prefix} ${clock} Remote content: ${colorize('Check server', ANSIColors.BLUE)}`\n );\n } else if (isRemoteDone) {\n lines.push(\n `${this.prefix} ${v} Remote content: ${colorize(`${remoteDone}`, ANSIColors.GREEN)}${colorize(`/${remoteTotal}`, ANSIColors.GREY)}`\n );\n } else {\n lines.push(\n `${this.prefix} ${clock} Remote content: ${colorize(`${remoteDone}`, ANSIColors.BLUE)}${colorize(`/${remoteTotal}`, ANSIColors.GREY)}`\n );\n }\n }\n\n // Plugin line: show error or progress counts, suffixed by the plugin names\n if (pluginTotal > 0 || this.pluginError) {\n const pluginNamesSuffix =\n this.pluginNames.length > 0\n ? colorize(` (${this.pluginNames.join(', ')})`, ANSIColors.GREY)\n : '';\n\n if (this.pluginError) {\n lines.push(\n `${this.prefix} ${x} Plugin content: ${colorize(\n this.pluginError,\n ANSIColors.RED\n )}`\n );\n } else if (isPluginDone) {\n lines.push(\n `${this.prefix} ${v} Plugin content: ${colorize(`${pluginDone}`, ANSIColors.GREEN)}${colorize(`/${pluginTotal}`, ANSIColors.GREY)}${pluginNamesSuffix}`\n );\n } else {\n lines.push(\n `${this.prefix} ${clock} Plugin content: ${colorize(`${pluginDone}`, ANSIColors.BLUE)}${colorize(`/${pluginTotal}`, ANSIColors.GREY)}${pluginNamesSuffix}`\n );\n }\n }\n\n // Check if the state has changed to avoid duplicate rendering\n const currentState = lines.join('\\n');\n if (currentState === this.lastRenderedState) {\n return;\n }\n this.lastRenderedState = currentState;\n\n if (this.renderedLines > 0) {\n process.stdout.write(`\\x1b[${this.renderedLines}F`);\n }\n\n const totalLinesToClear = Math.max(this.renderedLines, lines.length);\n for (let i = 0; i < totalLinesToClear; i++) {\n process.stdout.write('\\x1b[2K');\n const line = lines[i];\n if (line !== undefined) {\n process.stdout.write(line);\n }\n process.stdout.write('\\n');\n }\n\n this.renderedLines = lines.length;\n }\n\n private computeProgress() {\n const localKeys = new Set(\n this.statuses\n .filter((s) => s.type === 'local')\n .map((s) => s.dictionaryKey)\n );\n\n const localDoneKeys = new Set(\n this.statuses\n .filter(\n (s) =>\n s.type === 'local' && (s.status === 'built' || s.status === 'error')\n )\n .map((s) => s.dictionaryKey)\n );\n\n const remoteKeys = new Set(\n this.statuses\n .filter((s) => s.type === 'remote')\n .map((s) => s.dictionaryKey)\n );\n\n const remoteDoneKeys = new Set(\n this.statuses\n .filter(\n (s) =>\n s.type === 'remote' &&\n (s.status === 'fetched' ||\n s.status === 'imported' ||\n s.status === 'error')\n )\n .map((s) => s.dictionaryKey)\n );\n\n return {\n localTotal: localKeys.size,\n localDone: localDoneKeys.size,\n remoteTotal: remoteKeys.size,\n remoteDone: remoteDoneKeys.size,\n pluginTotal: this.pluginTotal,\n pluginDone: this.pluginDone,\n } as const;\n }\n}\n"],"mappings":"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"}
|
|
@@ -23,16 +23,17 @@ const logConfigDetails = (options) => {
|
|
|
23
23
|
if (customConfiguration) {
|
|
24
24
|
const validation = intlayerConfigSchema.safeParse(customConfiguration);
|
|
25
25
|
if (!validation.success) {
|
|
26
|
-
const
|
|
26
|
+
const errorMessages = validation.error.issues.map((error) => {
|
|
27
27
|
return `${colorizePath(` - ${error.path.join(".")}:`)} ${colorize(error.message, ANSIColors.GREY_DARK)}`;
|
|
28
|
-
}).join("\n")
|
|
28
|
+
}).join("\n");
|
|
29
|
+
const errorMessage = `${x} Invalid configuration:\n${errorMessages}`;
|
|
29
30
|
appLogger(errorMessage);
|
|
30
31
|
}
|
|
31
32
|
if (customConfiguration.build?.importMode) appLogger(`${colorize("build.importMode", ANSIColors.BLUE)} is deprecated, use ${colorize("dictionary.importMode", ANSIColors.BLUE)} instead`);
|
|
32
33
|
if (customConfiguration.compiler?.transformPattern) appLogger(`${colorize("compiler.transformPattern", ANSIColors.BLUE)} is deprecated, use ${colorize("build.traversePattern", ANSIColors.BLUE)} instead`);
|
|
33
34
|
if (customConfiguration.compiler?.excludePattern) appLogger(`${colorize("compiler.excludePattern", ANSIColors.BLUE)} is deprecated, use ${colorize("build.traversePattern", ANSIColors.BLUE)} instead`);
|
|
34
35
|
}
|
|
35
|
-
}, { cacheTimeoutMs:
|
|
36
|
+
}, { cacheTimeoutMs: 6e4 });
|
|
36
37
|
};
|
|
37
38
|
|
|
38
39
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logConfigDetails.mjs","names":[],"sources":["../../src/logConfigDetails.ts"],"sourcesContent":["import { join, relative } from 'node:path';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { getEnvFilePath } from '@intlayer/config/env';\nimport {\n colorize,\n colorizePath,\n getAppLogger,\n x,\n} from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfigurationAndFilePath,\n intlayerConfigSchema,\n} from '@intlayer/config/node';\nimport { formatPath, runOnce } from './utils';\n\nexport const logConfigDetails = (options?: GetConfigurationOptions) => {\n const {\n configuration,\n customConfiguration,\n numCustomConfiguration,\n configurationFilePath,\n } = getConfigurationAndFilePath(options);\n const appLogger = getAppLogger(configuration);\n\n runOnce(\n join(\n configuration.system.baseDir,\n '.intlayer',\n 'cache',\n 'intlayer-config-locaded.lock'\n ),\n () => {\n if (numCustomConfiguration === 0) {\n appLogger(\n 'Configuration file not found, using default configuration.',\n {\n isVerbose: true,\n }\n );\n } else {\n const baseDir = configuration.system.baseDir;\n const relativeOutputPath = relative(baseDir, configurationFilePath!);\n\n if (numCustomConfiguration === 1) {\n const dotEnvFilePath = getEnvFilePath(options?.env, options?.envFile);\n\n appLogger(\n `Configuration loaded ${formatPath(relativeOutputPath)}${dotEnvFilePath ? ` - Env: ${formatPath(dotEnvFilePath)}` : ''}`,\n {\n isVerbose: true,\n }\n );\n } else {\n appLogger(\n `Multiple configuration files found, using ${formatPath(relativeOutputPath)}.`,\n {\n isVerbose: true,\n }\n );\n }\n }\n\n if (customConfiguration) {\n const validation = intlayerConfigSchema.safeParse(customConfiguration);\n\n if (!validation.success) {\n const errorMessages = validation.error.issues\n .map((error) => {\n const path = colorizePath(` - ${error.path.join('.')}:`);\n const message = colorize(error.message, ANSIColors.GREY_DARK);\n return `${path} ${message}`;\n })\n .join('\\n');\n const errorMessage = `${x} Invalid configuration:\\n${errorMessages}`;\n\n appLogger(errorMessage);\n }\n\n if (customConfiguration.build?.importMode) {\n appLogger(\n `${colorize('build.importMode', ANSIColors.BLUE)} is deprecated, use ${colorize('dictionary.importMode', ANSIColors.BLUE)} instead`\n );\n }\n if (customConfiguration.compiler?.transformPattern) {\n appLogger(\n `${colorize('compiler.transformPattern', ANSIColors.BLUE)} is deprecated, use ${colorize('build.traversePattern', ANSIColors.BLUE)} instead`\n );\n }\n if (customConfiguration.compiler?.excludePattern) {\n appLogger(\n `${colorize('compiler.excludePattern', ANSIColors.BLUE)} is deprecated, use ${colorize('build.traversePattern', ANSIColors.BLUE)} instead`\n );\n }\n }\n },\n {\n cacheTimeoutMs: 1000 * 60, // 1 minute\n }\n );\n};\n"],"mappings":";;;;;;;;;AAgBA,MAAa,oBAAoB,YAAsC;CACrE,MAAM,EACJ,eACA,qBACA,wBACA,0BACE,4BAA4B,OAAO;CACvC,MAAM,YAAY,aAAa,aAAa;CAE5C,QACE,KACE,cAAc,OAAO,SACrB,aACA,SACA,8BACF,SACM;EACJ,IAAI,2BAA2B,GAC7B,UACE,8DACA,EACE,WAAW,KACb,CACF;OACK;GACL,MAAM,UAAU,cAAc,OAAO;GACrC,MAAM,qBAAqB,SAAS,SAAS,qBAAsB;GAEnE,IAAI,2BAA2B,GAAG;IAChC,MAAM,iBAAiB,eAAe,SAAS,KAAK,SAAS,OAAO;IAEpE,UACE,wBAAwB,WAAW,kBAAkB,IAAI,iBAAiB,WAAW,WAAW,cAAc,MAAM,MACpH,EACE,WAAW,KACb,CACF;GACF,OACE,UACE,6CAA6C,WAAW,kBAAkB,EAAE,IAC5E,EACE,WAAW,KACb,CACF;EAEJ;EAEA,IAAI,qBAAqB;GACvB,MAAM,aAAa,qBAAqB,UAAU,mBAAmB;GAErE,IAAI,CAAC,WAAW,SAAS;
|
|
1
|
+
{"version":3,"file":"logConfigDetails.mjs","names":[],"sources":["../../src/logConfigDetails.ts"],"sourcesContent":["import { join, relative } from 'node:path';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { getEnvFilePath } from '@intlayer/config/env';\nimport {\n colorize,\n colorizePath,\n getAppLogger,\n x,\n} from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfigurationAndFilePath,\n intlayerConfigSchema,\n} from '@intlayer/config/node';\nimport { formatPath, runOnce } from './utils';\n\nexport const logConfigDetails = (options?: GetConfigurationOptions) => {\n const {\n configuration,\n customConfiguration,\n numCustomConfiguration,\n configurationFilePath,\n } = getConfigurationAndFilePath(options);\n const appLogger = getAppLogger(configuration);\n\n runOnce(\n join(\n configuration.system.baseDir,\n '.intlayer',\n 'cache',\n 'intlayer-config-locaded.lock'\n ),\n () => {\n if (numCustomConfiguration === 0) {\n appLogger(\n 'Configuration file not found, using default configuration.',\n {\n isVerbose: true,\n }\n );\n } else {\n const baseDir = configuration.system.baseDir;\n const relativeOutputPath = relative(baseDir, configurationFilePath!);\n\n if (numCustomConfiguration === 1) {\n const dotEnvFilePath = getEnvFilePath(options?.env, options?.envFile);\n\n appLogger(\n `Configuration loaded ${formatPath(relativeOutputPath)}${dotEnvFilePath ? ` - Env: ${formatPath(dotEnvFilePath)}` : ''}`,\n {\n isVerbose: true,\n }\n );\n } else {\n appLogger(\n `Multiple configuration files found, using ${formatPath(relativeOutputPath)}.`,\n {\n isVerbose: true,\n }\n );\n }\n }\n\n if (customConfiguration) {\n const validation = intlayerConfigSchema.safeParse(customConfiguration);\n\n if (!validation.success) {\n const errorMessages = validation.error.issues\n .map((error) => {\n const path = colorizePath(` - ${error.path.join('.')}:`);\n const message = colorize(error.message, ANSIColors.GREY_DARK);\n return `${path} ${message}`;\n })\n .join('\\n');\n const errorMessage = `${x} Invalid configuration:\\n${errorMessages}`;\n\n appLogger(errorMessage);\n }\n\n if (customConfiguration.build?.importMode) {\n appLogger(\n `${colorize('build.importMode', ANSIColors.BLUE)} is deprecated, use ${colorize('dictionary.importMode', ANSIColors.BLUE)} instead`\n );\n }\n if (customConfiguration.compiler?.transformPattern) {\n appLogger(\n `${colorize('compiler.transformPattern', ANSIColors.BLUE)} is deprecated, use ${colorize('build.traversePattern', ANSIColors.BLUE)} instead`\n );\n }\n if (customConfiguration.compiler?.excludePattern) {\n appLogger(\n `${colorize('compiler.excludePattern', ANSIColors.BLUE)} is deprecated, use ${colorize('build.traversePattern', ANSIColors.BLUE)} instead`\n );\n }\n }\n },\n {\n cacheTimeoutMs: 1000 * 60, // 1 minute\n }\n );\n};\n"],"mappings":";;;;;;;;;AAgBA,MAAa,oBAAoB,YAAsC;CACrE,MAAM,EACJ,eACA,qBACA,wBACA,0BACE,4BAA4B,OAAO;CACvC,MAAM,YAAY,aAAa,aAAa;CAE5C,QACE,KACE,cAAc,OAAO,SACrB,aACA,SACA,8BACF,SACM;EACJ,IAAI,2BAA2B,GAC7B,UACE,8DACA,EACE,WAAW,KACb,CACF;OACK;GACL,MAAM,UAAU,cAAc,OAAO;GACrC,MAAM,qBAAqB,SAAS,SAAS,qBAAsB;GAEnE,IAAI,2BAA2B,GAAG;IAChC,MAAM,iBAAiB,eAAe,SAAS,KAAK,SAAS,OAAO;IAEpE,UACE,wBAAwB,WAAW,kBAAkB,IAAI,iBAAiB,WAAW,WAAW,cAAc,MAAM,MACpH,EACE,WAAW,KACb,CACF;GACF,OACE,UACE,6CAA6C,WAAW,kBAAkB,EAAE,IAC5E,EACE,WAAW,KACb,CACF;EAEJ;EAEA,IAAI,qBAAqB;GACvB,MAAM,aAAa,qBAAqB,UAAU,mBAAmB;GAErE,IAAI,CAAC,WAAW,SAAS;IACvB,MAAM,gBAAgB,WAAW,MAAM,OACpC,KAAK,UAAU;KAGd,OAAO,GAFM,aAAa,MAAM,MAAM,KAAK,KAAK,GAAG,EAAE,EAExC,EAAE,GADC,SAAS,MAAM,SAAS,WAAW,SAC3B;IAC1B,CAAC,CAAC,CACD,KAAK,IAAI;IACZ,MAAM,eAAe,GAAG,EAAE,2BAA2B;IAErD,UAAU,YAAY;GACxB;GAEA,IAAI,oBAAoB,OAAO,YAC7B,UACE,GAAG,SAAS,oBAAoB,WAAW,IAAI,EAAE,sBAAsB,SAAS,yBAAyB,WAAW,IAAI,EAAE,SAC5H;GAEF,IAAI,oBAAoB,UAAU,kBAChC,UACE,GAAG,SAAS,6BAA6B,WAAW,IAAI,EAAE,sBAAsB,SAAS,yBAAyB,WAAW,IAAI,EAAE,SACrI;GAEF,IAAI,oBAAoB,UAAU,gBAChC,UACE,GAAG,SAAS,2BAA2B,WAAW,IAAI,EAAE,sBAAsB,SAAS,yBAAyB,WAAW,IAAI,EAAE,SACnI;EAEJ;CACF,GACA,EACE,gBAAgB,IAClB,CACF;AACF"}
|
|
@@ -20,7 +20,7 @@ const DEFAULT_PREPARE_INTLAYER_OPTIONS = {
|
|
|
20
20
|
clean: false,
|
|
21
21
|
env: "dev",
|
|
22
22
|
format: ["cjs", "esm"],
|
|
23
|
-
cacheTimeoutMs:
|
|
23
|
+
cacheTimeoutMs: 36e5
|
|
24
24
|
};
|
|
25
25
|
const prepareIntlayer = async (configuration, options) => {
|
|
26
26
|
const appLogger = getAppLogger(configuration);
|
|
@@ -53,7 +53,8 @@ const prepareIntlayer = async (configuration, options) => {
|
|
|
53
53
|
await writeConfiguration(configuration);
|
|
54
54
|
const configurationWrittenTime = Date.now();
|
|
55
55
|
appLogger(["Configuration written", colorize(`(${configurationWrittenTime - preparationStartMs}ms)`, ANSIColors.GREY_DARK)], { isVerbose: true });
|
|
56
|
-
const
|
|
56
|
+
const contentDeclarationPaths = dictionariesWithStats.map((dictionary) => dictionary.path);
|
|
57
|
+
const dictionaries = await loadDictionaries(contentDeclarationPaths, configuration);
|
|
57
58
|
const dictionariesLoadedTime = Date.now();
|
|
58
59
|
appLogger(["Content loaded", colorize([dictionaries.remoteDictionaries.length + dictionaries.pluginDictionaries.length > 0 ? [
|
|
59
60
|
`(Total: ${dictionariesLoadedTime - configurationWrittenTime}ms`,
|
|
@@ -72,7 +73,8 @@ const prepareIntlayer = async (configuration, options) => {
|
|
|
72
73
|
env
|
|
73
74
|
});
|
|
74
75
|
await writeRemoteDictionary(dictionaries.remoteDictionaries, configuration);
|
|
75
|
-
|
|
76
|
+
const dictionariesToBuild = Object.values(dictionariesOutput?.mergedDictionaries ?? {}).map((dictionary) => dictionary.dictionary);
|
|
77
|
+
await createTypes(dictionariesToBuild, configuration);
|
|
76
78
|
await createDictionaryEntryPoint(configuration, { formats: options?.format });
|
|
77
79
|
const dictionariesBuiltTime = Date.now();
|
|
78
80
|
appLogger(["Dictionaries built", colorize(`(${dictionariesBuiltTime - preparationStartMs}ms)`, ANSIColors.GREY_DARK)]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prepareIntlayer.mjs","names":[],"sources":["../../src/prepareIntlayer.ts"],"sourcesContent":["import { stat } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport packageJson from '@intlayer/config/package.json' with { type: 'json' };\nimport { cacheDisk } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { buildDictionary } from './buildIntlayerDictionary/buildIntlayerDictionary';\nimport { writeRemoteDictionary } from './buildIntlayerDictionary/writeRemoteDictionary';\nimport { cleanOutputDir } from './cleanOutputDir';\nimport { createDictionaryEntryPoint } from './createDictionaryEntryPoint/createDictionaryEntryPoint';\nimport { createModuleAugmentation, createTypes } from './createType/index';\nimport { listDictionariesWithStats } from './listDictionariesPath';\nimport { loadDictionaries } from './loadDictionaries/loadDictionaries';\nimport { runOnce } from './utils/runOnce';\nimport {\n isCachedConfigurationUpToDate,\n writeConfiguration,\n} from './writeConfiguration';\n\ntype PrepareIntlayerOptions = {\n clean?: boolean;\n env?: 'prod' | 'dev';\n format?: ('cjs' | 'esm')[];\n forceRun?: boolean;\n cacheTimeoutMs?: number;\n onIsCached?: () => void | Promise<void>;\n};\n\nconst DEFAULT_PREPARE_INTLAYER_OPTIONS = {\n clean: false,\n env: 'dev',\n format: ['cjs', 'esm'],\n cacheTimeoutMs: 1000 * 60 * 60, // 1 hour\n} satisfies PrepareIntlayerOptions;\n\nexport const prepareIntlayer = async (\n configuration: IntlayerConfig,\n options?: PrepareIntlayerOptions\n) => {\n const appLogger = getAppLogger(configuration);\n\n const sentinelPath = join(\n configuration.system.cacheDir,\n 'intlayer-prepared.lock'\n );\n // Clean output dir if the intlayer version has changed\n const versionCache = cacheDisk(configuration, ['intlayer-version']);\n const intlayerCacheVersion = await versionCache.get();\n const isCorrectVersion = Boolean(\n intlayerCacheVersion === undefined ||\n intlayerCacheVersion === packageJson.version\n );\n\n const isConfigSimilar = await isCachedConfigurationUpToDate(configuration);\n\n // Check if any dictionary has been changed to force a new rebuild\n const dictionariesWithStats = await listDictionariesWithStats(configuration);\n let isDictionaryChanged = false;\n try {\n // Try catch as sentinel file may not exist yet\n const sentinelStats = await stat(sentinelPath);\n isDictionaryChanged = dictionariesWithStats.some(\n (dictionary) =>\n dictionary.stats.mtime.getTime() > sentinelStats.mtime.getTime()\n );\n } catch {}\n\n const resolvedPlugins = await Promise.all(configuration.plugins ?? []);\n const hasPluginLoadDictionaries = resolvedPlugins.some((plugin) =>\n Boolean(plugin.loadDictionaries)\n ); // Disable cache if any plugin because it can have custom behavior\n\n const { clean, format, forceRun, onIsCached, cacheTimeoutMs, env } = {\n ...DEFAULT_PREPARE_INTLAYER_OPTIONS,\n forceRun:\n !isCorrectVersion ||\n !isConfigSimilar ||\n isDictionaryChanged ||\n hasPluginLoadDictionaries,\n ...(options ?? {}),\n };\n\n // Skip preparation if it has already been done recently\n await runOnce(\n sentinelPath,\n async ({ renewLock }) => {\n // comment because of issue with next and webpack\n // await checkVersionsConsistency(configuration);\n\n // Re-read the cached version now that the lock is held: another process\n // may have upgraded the output directory while this call was queued, in\n // which case cleaning it would throw away a build that just completed.\n const isCorrectVersionUnderLock =\n (await versionCache.get()) === packageJson.version;\n\n if (clean || !isCorrectVersionUnderLock) {\n await cleanOutputDir(configuration);\n // `cleanOutputDir` wipes the cache directory the sentinel lives in.\n // Re-create it immediately, otherwise a concurrent process (e.g. the\n // `intlayer watch` CLI next to a dev server) sees no lock, starts its\n // own run, and cleans the output directory while this one writes.\n await renewLock();\n }\n\n await versionCache.set(packageJson.version);\n\n const preparationStartMs = Date.now();\n\n appLogger([\n 'Preparing Intlayer',\n colorize(`(v${packageJson.version})`, ANSIColors.GREY_DARK),\n ]);\n\n await writeConfiguration(configuration);\n\n const configurationWrittenTime = Date.now();\n\n appLogger(\n [\n 'Configuration written',\n colorize(\n `(${configurationWrittenTime - preparationStartMs}ms)`,\n ANSIColors.GREY_DARK\n ),\n ],\n {\n isVerbose: true,\n }\n );\n\n const contentDeclarationPaths = dictionariesWithStats.map(\n (dictionary) => dictionary.path\n );\n\n const dictionaries = await loadDictionaries(\n contentDeclarationPaths,\n configuration\n );\n\n const dictionariesLoadedTime = Date.now();\n\n appLogger(\n [\n 'Content loaded',\n colorize(\n [\n dictionaries.remoteDictionaries.length +\n dictionaries.pluginDictionaries.length >\n 0\n ? [\n `(Total: ${dictionariesLoadedTime - configurationWrittenTime}ms`,\n dictionaries.localDictionaries.length > 0\n ? ` - Local: ${dictionaries.time.localDictionaries}ms`\n : '',\n dictionaries.remoteDictionaries.length > 0\n ? ` - Remote: ${dictionaries.time.remoteDictionaries}ms`\n : '',\n dictionaries.pluginDictionaries.length > 0\n ? ` - Plugin: ${dictionaries.time.pluginDictionaries}ms`\n : '',\n `)`,\n ].join('')\n : `(${dictionariesLoadedTime - configurationWrittenTime}ms)`,\n ].join(''),\n ANSIColors.GREY_DARK\n ),\n ],\n {\n isVerbose: true,\n }\n );\n\n // Build local dictionaries\n const dictionariesOutput = await buildDictionary(\n [\n ...dictionaries.localDictionaries,\n ...dictionaries.remoteDictionaries,\n ...dictionaries.pluginDictionaries,\n ],\n configuration,\n { formats: format, importOtherDictionaries: false, env }\n );\n\n // Write remote dictionaries\n // Used as cache for next fetch\n await writeRemoteDictionary(\n dictionaries.remoteDictionaries,\n configuration\n );\n\n const dictionariesToBuild = Object.values(\n dictionariesOutput?.mergedDictionaries ?? {}\n ).map((dictionary) => dictionary.dictionary);\n\n await createTypes(dictionariesToBuild, configuration);\n\n await createDictionaryEntryPoint(configuration, {\n formats: options?.format,\n });\n\n const dictionariesBuiltTime = Date.now();\n\n appLogger([\n 'Dictionaries built',\n colorize(\n `(${dictionariesBuiltTime - preparationStartMs}ms)`,\n ANSIColors.GREY_DARK\n ),\n ]);\n\n await createModuleAugmentation(configuration);\n\n const moduleAugmentationBuiltTime = Date.now();\n\n appLogger(\n [\n 'Module augmentation built',\n colorize(\n `(${moduleAugmentationBuiltTime - dictionariesBuiltTime}ms)`,\n ANSIColors.GREY_DARK\n ),\n ],\n {\n isVerbose: true,\n }\n );\n\n // Plugin transformation\n // Allow plugins to post-process the final build output (e.g., write back ICU JSON)\n for await (const plugin of configuration.plugins ?? []) {\n const { unmergedDictionaries, mergedDictionaries } = dictionariesOutput;\n\n try {\n await plugin.afterBuild?.({\n dictionaries: {\n unmergedDictionaries,\n mergedDictionaries,\n },\n configuration,\n });\n } catch (error) {\n // A failing write-back (e.g. transient network error on a remote\n // target) must not invalidate the dictionaries already built.\n appLogger(\n [\n `Plugin ${plugin.name} afterBuild failed:`,\n (error as Error).message,\n ],\n { level: 'error' }\n );\n }\n }\n\n const preparationElapsedMs = Date.now() - preparationStartMs;\n appLogger(\n [`Done`, colorize(`${preparationElapsedMs}ms`, ANSIColors.GREEN)],\n {\n level: 'info',\n isVerbose: true,\n }\n );\n },\n {\n forceRun,\n onIsCached,\n cacheTimeoutMs,\n }\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"prepareIntlayer.mjs","names":[],"sources":["../../src/prepareIntlayer.ts"],"sourcesContent":["import { stat } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport packageJson from '@intlayer/config/package.json' with { type: 'json' };\nimport { cacheDisk } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { buildDictionary } from './buildIntlayerDictionary/buildIntlayerDictionary';\nimport { writeRemoteDictionary } from './buildIntlayerDictionary/writeRemoteDictionary';\nimport { cleanOutputDir } from './cleanOutputDir';\nimport { createDictionaryEntryPoint } from './createDictionaryEntryPoint/createDictionaryEntryPoint';\nimport { createModuleAugmentation, createTypes } from './createType/index';\nimport { listDictionariesWithStats } from './listDictionariesPath';\nimport { loadDictionaries } from './loadDictionaries/loadDictionaries';\nimport { runOnce } from './utils/runOnce';\nimport {\n isCachedConfigurationUpToDate,\n writeConfiguration,\n} from './writeConfiguration';\n\ntype PrepareIntlayerOptions = {\n clean?: boolean;\n env?: 'prod' | 'dev';\n format?: ('cjs' | 'esm')[];\n forceRun?: boolean;\n cacheTimeoutMs?: number;\n onIsCached?: () => void | Promise<void>;\n};\n\nconst DEFAULT_PREPARE_INTLAYER_OPTIONS = {\n clean: false,\n env: 'dev',\n format: ['cjs', 'esm'],\n cacheTimeoutMs: 1000 * 60 * 60, // 1 hour\n} satisfies PrepareIntlayerOptions;\n\nexport const prepareIntlayer = async (\n configuration: IntlayerConfig,\n options?: PrepareIntlayerOptions\n) => {\n const appLogger = getAppLogger(configuration);\n\n const sentinelPath = join(\n configuration.system.cacheDir,\n 'intlayer-prepared.lock'\n );\n // Clean output dir if the intlayer version has changed\n const versionCache = cacheDisk(configuration, ['intlayer-version']);\n const intlayerCacheVersion = await versionCache.get();\n const isCorrectVersion = Boolean(\n intlayerCacheVersion === undefined ||\n intlayerCacheVersion === packageJson.version\n );\n\n const isConfigSimilar = await isCachedConfigurationUpToDate(configuration);\n\n // Check if any dictionary has been changed to force a new rebuild\n const dictionariesWithStats = await listDictionariesWithStats(configuration);\n let isDictionaryChanged = false;\n try {\n // Try catch as sentinel file may not exist yet\n const sentinelStats = await stat(sentinelPath);\n isDictionaryChanged = dictionariesWithStats.some(\n (dictionary) =>\n dictionary.stats.mtime.getTime() > sentinelStats.mtime.getTime()\n );\n } catch {}\n\n const resolvedPlugins = await Promise.all(configuration.plugins ?? []);\n const hasPluginLoadDictionaries = resolvedPlugins.some((plugin) =>\n Boolean(plugin.loadDictionaries)\n ); // Disable cache if any plugin because it can have custom behavior\n\n const { clean, format, forceRun, onIsCached, cacheTimeoutMs, env } = {\n ...DEFAULT_PREPARE_INTLAYER_OPTIONS,\n forceRun:\n !isCorrectVersion ||\n !isConfigSimilar ||\n isDictionaryChanged ||\n hasPluginLoadDictionaries,\n ...(options ?? {}),\n };\n\n // Skip preparation if it has already been done recently\n await runOnce(\n sentinelPath,\n async ({ renewLock }) => {\n // comment because of issue with next and webpack\n // await checkVersionsConsistency(configuration);\n\n // Re-read the cached version now that the lock is held: another process\n // may have upgraded the output directory while this call was queued, in\n // which case cleaning it would throw away a build that just completed.\n const isCorrectVersionUnderLock =\n (await versionCache.get()) === packageJson.version;\n\n if (clean || !isCorrectVersionUnderLock) {\n await cleanOutputDir(configuration);\n // `cleanOutputDir` wipes the cache directory the sentinel lives in.\n // Re-create it immediately, otherwise a concurrent process (e.g. the\n // `intlayer watch` CLI next to a dev server) sees no lock, starts its\n // own run, and cleans the output directory while this one writes.\n await renewLock();\n }\n\n await versionCache.set(packageJson.version);\n\n const preparationStartMs = Date.now();\n\n appLogger([\n 'Preparing Intlayer',\n colorize(`(v${packageJson.version})`, ANSIColors.GREY_DARK),\n ]);\n\n await writeConfiguration(configuration);\n\n const configurationWrittenTime = Date.now();\n\n appLogger(\n [\n 'Configuration written',\n colorize(\n `(${configurationWrittenTime - preparationStartMs}ms)`,\n ANSIColors.GREY_DARK\n ),\n ],\n {\n isVerbose: true,\n }\n );\n\n const contentDeclarationPaths = dictionariesWithStats.map(\n (dictionary) => dictionary.path\n );\n\n const dictionaries = await loadDictionaries(\n contentDeclarationPaths,\n configuration\n );\n\n const dictionariesLoadedTime = Date.now();\n\n appLogger(\n [\n 'Content loaded',\n colorize(\n [\n dictionaries.remoteDictionaries.length +\n dictionaries.pluginDictionaries.length >\n 0\n ? [\n `(Total: ${dictionariesLoadedTime - configurationWrittenTime}ms`,\n dictionaries.localDictionaries.length > 0\n ? ` - Local: ${dictionaries.time.localDictionaries}ms`\n : '',\n dictionaries.remoteDictionaries.length > 0\n ? ` - Remote: ${dictionaries.time.remoteDictionaries}ms`\n : '',\n dictionaries.pluginDictionaries.length > 0\n ? ` - Plugin: ${dictionaries.time.pluginDictionaries}ms`\n : '',\n `)`,\n ].join('')\n : `(${dictionariesLoadedTime - configurationWrittenTime}ms)`,\n ].join(''),\n ANSIColors.GREY_DARK\n ),\n ],\n {\n isVerbose: true,\n }\n );\n\n // Build local dictionaries\n const dictionariesOutput = await buildDictionary(\n [\n ...dictionaries.localDictionaries,\n ...dictionaries.remoteDictionaries,\n ...dictionaries.pluginDictionaries,\n ],\n configuration,\n { formats: format, importOtherDictionaries: false, env }\n );\n\n // Write remote dictionaries\n // Used as cache for next fetch\n await writeRemoteDictionary(\n dictionaries.remoteDictionaries,\n configuration\n );\n\n const dictionariesToBuild = Object.values(\n dictionariesOutput?.mergedDictionaries ?? {}\n ).map((dictionary) => dictionary.dictionary);\n\n await createTypes(dictionariesToBuild, configuration);\n\n await createDictionaryEntryPoint(configuration, {\n formats: options?.format,\n });\n\n const dictionariesBuiltTime = Date.now();\n\n appLogger([\n 'Dictionaries built',\n colorize(\n `(${dictionariesBuiltTime - preparationStartMs}ms)`,\n ANSIColors.GREY_DARK\n ),\n ]);\n\n await createModuleAugmentation(configuration);\n\n const moduleAugmentationBuiltTime = Date.now();\n\n appLogger(\n [\n 'Module augmentation built',\n colorize(\n `(${moduleAugmentationBuiltTime - dictionariesBuiltTime}ms)`,\n ANSIColors.GREY_DARK\n ),\n ],\n {\n isVerbose: true,\n }\n );\n\n // Plugin transformation\n // Allow plugins to post-process the final build output (e.g., write back ICU JSON)\n for await (const plugin of configuration.plugins ?? []) {\n const { unmergedDictionaries, mergedDictionaries } = dictionariesOutput;\n\n try {\n await plugin.afterBuild?.({\n dictionaries: {\n unmergedDictionaries,\n mergedDictionaries,\n },\n configuration,\n });\n } catch (error) {\n // A failing write-back (e.g. transient network error on a remote\n // target) must not invalidate the dictionaries already built.\n appLogger(\n [\n `Plugin ${plugin.name} afterBuild failed:`,\n (error as Error).message,\n ],\n { level: 'error' }\n );\n }\n }\n\n const preparationElapsedMs = Date.now() - preparationStartMs;\n appLogger(\n [`Done`, colorize(`${preparationElapsedMs}ms`, ANSIColors.GREEN)],\n {\n level: 'info',\n isVerbose: true,\n }\n );\n },\n {\n forceRun,\n onIsCached,\n cacheTimeoutMs,\n }\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AA6BA,MAAM,mCAAmC;CACvC,OAAO;CACP,KAAK;CACL,QAAQ,CAAC,OAAO,KAAK;CACrB,gBAAgB;AAClB;AAEA,MAAa,kBAAkB,OAC7B,eACA,YACG;CACH,MAAM,YAAY,aAAa,aAAa;CAE5C,MAAM,eAAe,KACnB,cAAc,OAAO,UACrB,wBACF;CAEA,MAAM,eAAe,UAAU,eAAe,CAAC,kBAAkB,CAAC;CAClE,MAAM,uBAAuB,MAAM,aAAa,IAAI;CACpD,MAAM,mBAAmB,QACvB,yBAAyB,UACvB,yBAAyB,YAAY,OACzC;CAEA,MAAM,kBAAkB,MAAM,8BAA8B,aAAa;CAGzE,MAAM,wBAAwB,MAAM,0BAA0B,aAAa;CAC3E,IAAI,sBAAsB;CAC1B,IAAI;EAEF,MAAM,gBAAgB,MAAM,KAAK,YAAY;EAC7C,sBAAsB,sBAAsB,MACzC,eACC,WAAW,MAAM,MAAM,QAAQ,IAAI,cAAc,MAAM,QAAQ,CACnE;CACF,QAAQ,CAAC;CAGT,MAAM,6BAA4B,MADJ,QAAQ,IAAI,cAAc,WAAW,CAAC,CAAC,EACpB,CAAC,MAAM,WACtD,QAAQ,OAAO,gBAAgB,CACjC;CAEA,MAAM,EAAE,OAAO,QAAQ,UAAU,YAAY,gBAAgB,QAAQ;EACnE,GAAG;EACH,UACE,CAAC,oBACD,CAAC,mBACD,uBACA;EACF,GAAI,WAAW,CAAC;CAClB;CAGA,MAAM,QACJ,cACA,OAAO,EAAE,gBAAgB;EAOvB,MAAM,4BACH,MAAM,aAAa,IAAI,MAAO,YAAY;EAE7C,IAAI,SAAS,CAAC,2BAA2B;GACvC,MAAM,eAAe,aAAa;GAKlC,MAAM,UAAU;EAClB;EAEA,MAAM,aAAa,IAAI,YAAY,OAAO;EAE1C,MAAM,qBAAqB,KAAK,IAAI;EAEpC,UAAU,CACR,sBACA,SAAS,KAAK,YAAY,QAAQ,IAAI,WAAW,SAAS,CAC5D,CAAC;EAED,MAAM,mBAAmB,aAAa;EAEtC,MAAM,2BAA2B,KAAK,IAAI;EAE1C,UACE,CACE,yBACA,SACE,IAAI,2BAA2B,mBAAmB,MAClD,WAAW,SACb,CACF,GACA,EACE,WAAW,KACb,CACF;EAEA,MAAM,0BAA0B,sBAAsB,KACnD,eAAe,WAAW,IAC7B;EAEA,MAAM,eAAe,MAAM,iBACzB,yBACA,aACF;EAEA,MAAM,yBAAyB,KAAK,IAAI;EAExC,UACE,CACE,kBACA,SACE,CACE,aAAa,mBAAmB,SAC9B,aAAa,mBAAmB,SAClC,IACI;GACE,WAAW,yBAAyB,yBAAyB;GAC7D,aAAa,kBAAkB,SAAS,IACpC,aAAa,aAAa,KAAK,kBAAkB,MACjD;GACJ,aAAa,mBAAmB,SAAS,IACrC,cAAc,aAAa,KAAK,mBAAmB,MACnD;GACJ,aAAa,mBAAmB,SAAS,IACrC,cAAc,aAAa,KAAK,mBAAmB,MACnD;GACJ;EACF,CAAC,CAAC,KAAK,EAAE,IACT,IAAI,yBAAyB,yBAAyB,IAC5D,CAAC,CAAC,KAAK,EAAE,GACT,WAAW,SACb,CACF,GACA,EACE,WAAW,KACb,CACF;EAGA,MAAM,qBAAqB,MAAM,gBAC/B;GACE,GAAG,aAAa;GAChB,GAAG,aAAa;GAChB,GAAG,aAAa;EAClB,GACA,eACA;GAAE,SAAS;GAAQ,yBAAyB;GAAO;EAAI,CACzD;EAIA,MAAM,sBACJ,aAAa,oBACb,aACF;EAEA,MAAM,sBAAsB,OAAO,OACjC,oBAAoB,sBAAsB,CAAC,CAC7C,CAAC,CAAC,KAAK,eAAe,WAAW,UAAU;EAE3C,MAAM,YAAY,qBAAqB,aAAa;EAEpD,MAAM,2BAA2B,eAAe,EAC9C,SAAS,SAAS,OACpB,CAAC;EAED,MAAM,wBAAwB,KAAK,IAAI;EAEvC,UAAU,CACR,sBACA,SACE,IAAI,wBAAwB,mBAAmB,MAC/C,WAAW,SACb,CACF,CAAC;EAED,MAAM,yBAAyB,aAAa;EAI5C,UACE,CACE,6BACA,SACE,IAN8B,KAAK,IAML,IAAI,sBAAsB,MACxD,WAAW,SACb,CACF,GACA,EACE,WAAW,KACb,CACF;EAIA,WAAW,MAAM,UAAU,cAAc,WAAW,CAAC,GAAG;GACtD,MAAM,EAAE,sBAAsB,uBAAuB;GAErD,IAAI;IACF,MAAM,OAAO,aAAa;KACxB,cAAc;MACZ;MACA;KACF;KACA;IACF,CAAC;GACH,SAAS,OAAO;IAGd,UACE,CACE,UAAU,OAAO,KAAK,sBACrB,MAAgB,OACnB,GACA,EAAE,OAAO,QAAQ,CACnB;GACF;EACF;EAEA,MAAM,uBAAuB,KAAK,IAAI,IAAI;EAC1C,UACE,CAAC,QAAQ,SAAS,GAAG,qBAAqB,KAAK,WAAW,KAAK,CAAC,GAChE;GACE,OAAO;GACP,WAAW;EACb,CACF;CACF,GACA;EACE;EACA;EACA;CACF,CACF;AACF"}
|
|
@@ -3,7 +3,8 @@ import { getMaskContent } from "@intlayer/core/plugins";
|
|
|
3
3
|
|
|
4
4
|
//#region src/reduceDictionaryContent/reduceDictionaryContent.ts
|
|
5
5
|
const reduceDictionaryContent = (fullDictionary, partialDictionary) => {
|
|
6
|
-
|
|
6
|
+
const mask = getMaskContent(partialDictionary);
|
|
7
|
+
return applyMask(fullDictionary, mask);
|
|
7
8
|
};
|
|
8
9
|
|
|
9
10
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reduceDictionaryContent.mjs","names":[],"sources":["../../../src/reduceDictionaryContent/reduceDictionaryContent.ts"],"sourcesContent":["import { getMaskContent } from '@intlayer/core/plugins';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { applyMask } from './applyMask';\n\nexport const reduceDictionaryContent = (\n fullDictionary: Dictionary,\n partialDictionary: Dictionary\n) => {\n const mask = getMaskContent(partialDictionary);\n const result = applyMask(fullDictionary, mask);\n\n return result;\n};\n"],"mappings":";;;;AAIA,MAAa,2BACX,gBACA,sBACG;
|
|
1
|
+
{"version":3,"file":"reduceDictionaryContent.mjs","names":[],"sources":["../../../src/reduceDictionaryContent/reduceDictionaryContent.ts"],"sourcesContent":["import { getMaskContent } from '@intlayer/core/plugins';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { applyMask } from './applyMask';\n\nexport const reduceDictionaryContent = (\n fullDictionary: Dictionary,\n partialDictionary: Dictionary\n) => {\n const mask = getMaskContent(partialDictionary);\n const result = applyMask(fullDictionary, mask);\n\n return result;\n};\n"],"mappings":";;;;AAIA,MAAa,2BACX,gBACA,sBACG;CACH,MAAM,OAAO,eAAe,iBAAiB;CAG7C,OAFe,UAAU,gBAAgB,IAE7B;AACd"}
|
package/dist/esm/scan/checks.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { ALL_LOCALES } from "@intlayer/types/allLocales";
|
|
|
5
5
|
//#region src/scan/checks.ts
|
|
6
6
|
/** Format a byte count as a human-readable size. */
|
|
7
7
|
const formatSize = (bytes) => {
|
|
8
|
-
if (bytes >=
|
|
8
|
+
if (bytes >= 1048576) return `${(bytes / 1048576).toFixed(2)} MB`;
|
|
9
9
|
if (bytes >= 1024) return `${(bytes / 1024).toFixed(2)} KB`;
|
|
10
10
|
return `${bytes} B`;
|
|
11
11
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checks.mjs","names":[],"sources":["../../../src/scan/checks.ts"],"sourcesContent":["import { ALL_LOCALES } from '@intlayer/types/allLocales';\nimport { analyzeBundleContent } from './analyzeBundleContent';\nimport {\n type Anchor,\n extractHreflangs,\n extractHtmlDir,\n extractHtmlLang,\n hasCanonical,\n} from './parseHtml';\nimport type {\n BundleChunkInput,\n BundleContentAnalysis,\n ScanEvent,\n} from './types';\n\n/** Format a byte count as a human-readable size. */\nexport const formatSize = (bytes: number): string => {\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(2)} KB`;\n return `${bytes} B`;\n};\n\n/**\n * Check the `<html>` element attributes (`lang`, `dir`) and the resulting\n * current-locale signal. Returns the detected language tag.\n */\nexport const checkHtmlAttributes = (\n html: string,\n targetUrl: string,\n events: ScanEvent[]\n): { langTag: string | undefined } => {\n const langTag = extractHtmlLang(html);\n const dirTag = extractHtmlDir(html);\n\n events.push({\n type: `url_htmlLang\\\\${targetUrl}`,\n status: langTag ? 'success' : 'error',\n details: {\n success: langTag,\n error: langTag ? undefined : 'Missing html lang attribute',\n },\n });\n\n events.push({\n type: `url_currentLocale\\\\${targetUrl}`,\n status: langTag ? 'success' : 'warning',\n details: {\n success: langTag,\n warning: langTag ? undefined : 'No locale detected',\n },\n });\n\n events.push({\n type: `url_htmlDir\\\\${targetUrl}`,\n status: dirTag ? 'success' : 'warning',\n details: {\n success: dirTag,\n warning: dirTag ? undefined : 'Missing html dir attribute',\n },\n });\n\n return { langTag };\n};\n\n/** Check the presence of a canonical link. */\nexport const checkCanonical = (\n html: string,\n targetUrl: string,\n events: ScanEvent[]\n): void => {\n const present = hasCanonical(html);\n events.push({\n type: `url_hasCanonical\\\\${targetUrl}`,\n status: present ? 'success' : 'warning',\n details: { warning: present ? undefined : 'Missing canonical link' },\n });\n};\n\n/**\n * Check the page's hreflang structure and collect discovered locales into\n * `localesSet`.\n */\nexport const checkLinguisticStructure = (\n html: string,\n targetUrl: string,\n localesSet: Set<string>,\n events: ScanEvent[]\n): void => {\n const langTag = extractHtmlLang(html);\n if (langTag) localesSet.add(langTag);\n\n const hreflangs = extractHreflangs(html);\n for (const { hreflang } of hreflangs) {\n if (hreflang !== 'x-default') localesSet.add(hreflang);\n }\n\n const hasXDefault = hreflangs.some((h) => h.hreflang === 'x-default');\n\n events.push({\n type: `url_hreflang\\\\${targetUrl}`,\n status: hreflangs.length > 0 ? 'success' : 'warning',\n details: {\n success: hreflangs.length > 0 ? hreflangs : undefined,\n warning: hreflangs.length === 0 ? 'No hreflang tags found' : undefined,\n },\n });\n\n events.push({\n type: `url_hasXDefault\\\\${targetUrl}`,\n status: hasXDefault ? 'success' : 'error',\n details: {\n error: hasXDefault ? undefined : 'Missing x-default hreflang link',\n },\n });\n};\n\nconst normalizeHost = (host: string): string =>\n host.startsWith('www.') ? host.slice(4) : host;\n\nconst localeValues = Object.values(ALL_LOCALES) as string[];\n\n/**\n * Check whether internal links carry a locale segment, mirroring the hosted\n * audit's URL-structure analysis but operating on parsed anchors instead of a\n * live DOM.\n */\nexport const checkUrlStructure = (\n anchors: Anchor[],\n origin: string,\n targetUrl: string,\n events: ScanEvent[]\n): void => {\n const targetHostname = normalizeHost(new URL(origin).hostname);\n\n let localizedCount = 0;\n let totalInternalCount = 0;\n const nonLocalizedLinks: string[] = [];\n\n for (const { href } of anchors) {\n if (!href || href.startsWith('#') || href.startsWith('javascript:'))\n continue;\n\n try {\n const url = new URL(href, origin);\n if (normalizeHost(url.hostname) !== targetHostname) continue;\n\n totalInternalCount++;\n const path = url.pathname.toLowerCase();\n const hostname = url.hostname.toLowerCase();\n\n const hasLocaleInPath = localeValues.some((locale) => {\n const l = locale.toLowerCase();\n return path === `/${l}` || path.includes(`/${l}/`);\n });\n const hasLocaleInSubdomain = localeValues.some((locale) => {\n const l = locale.toLowerCase();\n return hostname.startsWith(`${l}.`) || hostname.includes(`.${l}.`);\n });\n\n if (hasLocaleInPath || hasLocaleInSubdomain) {\n localizedCount++;\n } else {\n nonLocalizedLinks.push(href);\n }\n } catch {\n /* ignore malformed URLs */\n }\n }\n\n const hasLocalizedLinks = localizedCount > 0;\n const allAnchorsLocalized =\n totalInternalCount === 0 || localizedCount === totalInternalCount;\n\n events.push({\n type: `url_hasLocalizedLinks\\\\${targetUrl}`,\n status: hasLocalizedLinks ? 'success' : 'warning',\n details: {\n success: hasLocalizedLinks\n ? `${localizedCount} localized links found out of ${totalInternalCount} internal links`\n : undefined,\n warning: hasLocalizedLinks ? undefined : 'No localized links found',\n },\n });\n\n events.push({\n type: `url_allAnchorsLocalized\\\\${targetUrl}`,\n status: allAnchorsLocalized ? 'success' : 'warning',\n details: {\n warning: allAnchorsLocalized\n ? undefined\n : {\n message: 'Some internal links are not localized',\n links: nonLocalizedLinks,\n },\n },\n });\n};\n\n/**\n * Analyze the JavaScript bundles for unused locale content and emit the\n * corresponding event. Returns the analysis so callers can report on it.\n */\nexport const checkBundleContent = (\n chunks: BundleChunkInput[],\n html: string,\n currentLocale: string | undefined,\n targetUrl: string,\n totalPageSize: number,\n events: ScanEvent[]\n): BundleContentAnalysis | undefined => {\n if (!currentLocale) {\n events.push({\n type: `url_unusedBundleContent\\\\${targetUrl}`,\n status: 'warning',\n details: {\n warning: 'Cannot analyse bundle content: page locale not detected',\n },\n });\n return undefined;\n }\n\n const analysis = analyzeBundleContent(\n chunks,\n html,\n currentLocale,\n totalPageSize\n );\n\n // Status is driven by the main bundle — lazy chunks with unused content are expected.\n const mainBundleMaxUnused = analysis.mainBundleChunks.reduce(\n (max, c) => Math.max(max, c.unusedPercent),\n 0\n );\n\n const status =\n mainBundleMaxUnused === 0\n ? 'success'\n : mainBundleMaxUnused <= 30\n ? 'warning'\n : 'error';\n\n events.push({\n type: `url_unusedBundleContent\\\\${targetUrl}`,\n status,\n details: { [status]: analysis },\n });\n\n return analysis;\n};\n\n/** Fetch and check `robots.txt`, emitting robots-related events. */\nexport const checkRobots = async (\n origin: string,\n discoveredLocales: Set<string>,\n userAgent: string,\n events: ScanEvent[]\n): Promise<void> => {\n let robotsPresent = false;\n let noLocalizedUrlsForgotten = true;\n const errors: string[] = [];\n\n try {\n const response = await fetch(`${origin}/robots.txt`, {\n headers: { 'User-Agent': userAgent },\n });\n\n if (response.ok) {\n robotsPresent = true;\n const content = await response.text();\n\n if (content && discoveredLocales.size > 0) {\n const disallowedPaths = content\n .split('\\n')\n .map((line) => line.trim().toLowerCase())\n .filter((line) => line.startsWith('disallow:'))\n .map((line) => line.slice('disallow:'.length).trim());\n\n for (const locale of discoveredLocales) {\n for (const path of disallowedPaths) {\n if (path === `/${locale}` || path === `/${locale}/`) {\n noLocalizedUrlsForgotten = false;\n errors.push(\n `Locale path \"${locale}\" appears to be blocked in robots.txt: ${path}`\n );\n }\n }\n }\n }\n }\n } catch (error) {\n errors.push(\n `Failed to fetch robots.txt: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n\n events.push({\n type: 'robots_robotsPresent',\n status: robotsPresent ? 'success' : 'warning',\n details: {\n warning: robotsPresent ? undefined : 'No robots.txt found',\n error: errors.length > 0 ? errors : undefined,\n },\n });\n\n if (robotsPresent) {\n events.push({\n type: 'robots_noLocalizedUrlsForgotten',\n status: noLocalizedUrlsForgotten ? 'success' : 'error',\n details: { error: noLocalizedUrlsForgotten ? undefined : errors },\n });\n }\n};\n\n/** Fetch and check `sitemap.xml`, emitting sitemap-related events. */\nexport const checkSitemap = async (\n origin: string,\n discoveredLocales: Set<string>,\n userAgent: string,\n events: ScanEvent[]\n): Promise<void> => {\n let sitemapPresent = false;\n let hasXDefault = false;\n let hasAlternates = false;\n let noLocalizedUrlsForgotten = true;\n const errors: string[] = [];\n\n try {\n const response = await fetch(`${origin}/sitemap.xml`, {\n headers: { 'User-Agent': userAgent },\n });\n\n if (response.ok) {\n sitemapPresent = true;\n const content = await response.text();\n\n const hreflangMatches = content.match(/hreflang\\s*=\\s*\"([^\"]+)\"/gi) ?? [];\n const hreflangs = hreflangMatches.map((m) =>\n m.replace(/hreflang\\s*=\\s*\"/i, '').replace(/\"$/, '')\n );\n hasAlternates = hreflangs.length > 0;\n hasXDefault = hreflangs.includes('x-default');\n\n if (discoveredLocales.size > 0) {\n const urlBlocks = content.match(/<url\\b[\\s\\S]*?<\\/url>/gi) ?? [];\n const allFoundLocales = new Set<string>();\n let anyUrlMissingLocale = false;\n\n for (const block of urlBlocks) {\n const localesInUrl = new Set<string>();\n\n for (const hreflang of block.match(/hreflang\\s*=\\s*\"([^\"]+)\"/gi) ??\n []) {\n const value = hreflang\n .replace(/hreflang\\s*=\\s*\"/i, '')\n .replace(/\"$/, '');\n if (value !== 'x-default') {\n localesInUrl.add(value);\n allFoundLocales.add(value);\n }\n }\n\n const loc = block.match(/<loc>([\\s\\S]*?)<\\/loc>/i)?.[1]?.trim();\n if (loc) {\n try {\n const firstSegment = new URL(loc).pathname\n .split('/')\n .filter(Boolean)[0];\n if (firstSegment && discoveredLocales.has(firstSegment)) {\n localesInUrl.add(firstSegment);\n allFoundLocales.add(firstSegment);\n }\n } catch {\n /* invalid loc URL, skip */\n }\n }\n\n const missing = [...discoveredLocales].filter(\n (locale) => !localesInUrl.has(locale)\n );\n if (missing.length > 0 && missing.length < discoveredLocales.size) {\n anyUrlMissingLocale = true;\n }\n }\n\n const completelyMissing = [...discoveredLocales].filter(\n (locale) => !allFoundLocales.has(locale)\n );\n if (anyUrlMissingLocale || completelyMissing.length > 0) {\n noLocalizedUrlsForgotten = false;\n if (completelyMissing.length > 0) {\n errors.push(\n `The following locales are completely missing from the sitemap: ${completelyMissing.join(', ')}`\n );\n }\n }\n }\n }\n } catch (error) {\n errors.push(\n `Failed to fetch sitemap.xml: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n\n events.push({\n type: 'sitemap_sitemapPresent',\n status: sitemapPresent ? 'success' : 'warning',\n details: {\n warning: sitemapPresent ? undefined : 'No sitemap.xml found',\n error: errors.length > 0 ? errors : undefined,\n },\n });\n\n if (sitemapPresent) {\n events.push({\n type: 'sitemap_noLocalizedUrlsForgotten',\n status: noLocalizedUrlsForgotten ? 'success' : 'warning',\n details: { warning: noLocalizedUrlsForgotten ? undefined : errors },\n });\n\n events.push({\n type: 'sitemap_hasXDefault',\n status: hasXDefault ? 'success' : 'warning',\n details: {\n warning: hasXDefault ? undefined : 'No x-default hreflang in sitemap',\n },\n });\n\n events.push({\n type: 'sitemap_hasAlternates',\n status: hasAlternates ? 'success' : 'warning',\n details: {\n warning: hasAlternates\n ? undefined\n : 'No alternate language links found in sitemap',\n },\n });\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"checks.mjs","names":[],"sources":["../../../src/scan/checks.ts"],"sourcesContent":["import { ALL_LOCALES } from '@intlayer/types/allLocales';\nimport { analyzeBundleContent } from './analyzeBundleContent';\nimport {\n type Anchor,\n extractHreflangs,\n extractHtmlDir,\n extractHtmlLang,\n hasCanonical,\n} from './parseHtml';\nimport type {\n BundleChunkInput,\n BundleContentAnalysis,\n ScanEvent,\n} from './types';\n\n/** Format a byte count as a human-readable size. */\nexport const formatSize = (bytes: number): string => {\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(2)} KB`;\n return `${bytes} B`;\n};\n\n/**\n * Check the `<html>` element attributes (`lang`, `dir`) and the resulting\n * current-locale signal. Returns the detected language tag.\n */\nexport const checkHtmlAttributes = (\n html: string,\n targetUrl: string,\n events: ScanEvent[]\n): { langTag: string | undefined } => {\n const langTag = extractHtmlLang(html);\n const dirTag = extractHtmlDir(html);\n\n events.push({\n type: `url_htmlLang\\\\${targetUrl}`,\n status: langTag ? 'success' : 'error',\n details: {\n success: langTag,\n error: langTag ? undefined : 'Missing html lang attribute',\n },\n });\n\n events.push({\n type: `url_currentLocale\\\\${targetUrl}`,\n status: langTag ? 'success' : 'warning',\n details: {\n success: langTag,\n warning: langTag ? undefined : 'No locale detected',\n },\n });\n\n events.push({\n type: `url_htmlDir\\\\${targetUrl}`,\n status: dirTag ? 'success' : 'warning',\n details: {\n success: dirTag,\n warning: dirTag ? undefined : 'Missing html dir attribute',\n },\n });\n\n return { langTag };\n};\n\n/** Check the presence of a canonical link. */\nexport const checkCanonical = (\n html: string,\n targetUrl: string,\n events: ScanEvent[]\n): void => {\n const present = hasCanonical(html);\n events.push({\n type: `url_hasCanonical\\\\${targetUrl}`,\n status: present ? 'success' : 'warning',\n details: { warning: present ? undefined : 'Missing canonical link' },\n });\n};\n\n/**\n * Check the page's hreflang structure and collect discovered locales into\n * `localesSet`.\n */\nexport const checkLinguisticStructure = (\n html: string,\n targetUrl: string,\n localesSet: Set<string>,\n events: ScanEvent[]\n): void => {\n const langTag = extractHtmlLang(html);\n if (langTag) localesSet.add(langTag);\n\n const hreflangs = extractHreflangs(html);\n for (const { hreflang } of hreflangs) {\n if (hreflang !== 'x-default') localesSet.add(hreflang);\n }\n\n const hasXDefault = hreflangs.some((h) => h.hreflang === 'x-default');\n\n events.push({\n type: `url_hreflang\\\\${targetUrl}`,\n status: hreflangs.length > 0 ? 'success' : 'warning',\n details: {\n success: hreflangs.length > 0 ? hreflangs : undefined,\n warning: hreflangs.length === 0 ? 'No hreflang tags found' : undefined,\n },\n });\n\n events.push({\n type: `url_hasXDefault\\\\${targetUrl}`,\n status: hasXDefault ? 'success' : 'error',\n details: {\n error: hasXDefault ? undefined : 'Missing x-default hreflang link',\n },\n });\n};\n\nconst normalizeHost = (host: string): string =>\n host.startsWith('www.') ? host.slice(4) : host;\n\nconst localeValues = Object.values(ALL_LOCALES) as string[];\n\n/**\n * Check whether internal links carry a locale segment, mirroring the hosted\n * audit's URL-structure analysis but operating on parsed anchors instead of a\n * live DOM.\n */\nexport const checkUrlStructure = (\n anchors: Anchor[],\n origin: string,\n targetUrl: string,\n events: ScanEvent[]\n): void => {\n const targetHostname = normalizeHost(new URL(origin).hostname);\n\n let localizedCount = 0;\n let totalInternalCount = 0;\n const nonLocalizedLinks: string[] = [];\n\n for (const { href } of anchors) {\n if (!href || href.startsWith('#') || href.startsWith('javascript:'))\n continue;\n\n try {\n const url = new URL(href, origin);\n if (normalizeHost(url.hostname) !== targetHostname) continue;\n\n totalInternalCount++;\n const path = url.pathname.toLowerCase();\n const hostname = url.hostname.toLowerCase();\n\n const hasLocaleInPath = localeValues.some((locale) => {\n const l = locale.toLowerCase();\n return path === `/${l}` || path.includes(`/${l}/`);\n });\n const hasLocaleInSubdomain = localeValues.some((locale) => {\n const l = locale.toLowerCase();\n return hostname.startsWith(`${l}.`) || hostname.includes(`.${l}.`);\n });\n\n if (hasLocaleInPath || hasLocaleInSubdomain) {\n localizedCount++;\n } else {\n nonLocalizedLinks.push(href);\n }\n } catch {\n /* ignore malformed URLs */\n }\n }\n\n const hasLocalizedLinks = localizedCount > 0;\n const allAnchorsLocalized =\n totalInternalCount === 0 || localizedCount === totalInternalCount;\n\n events.push({\n type: `url_hasLocalizedLinks\\\\${targetUrl}`,\n status: hasLocalizedLinks ? 'success' : 'warning',\n details: {\n success: hasLocalizedLinks\n ? `${localizedCount} localized links found out of ${totalInternalCount} internal links`\n : undefined,\n warning: hasLocalizedLinks ? undefined : 'No localized links found',\n },\n });\n\n events.push({\n type: `url_allAnchorsLocalized\\\\${targetUrl}`,\n status: allAnchorsLocalized ? 'success' : 'warning',\n details: {\n warning: allAnchorsLocalized\n ? undefined\n : {\n message: 'Some internal links are not localized',\n links: nonLocalizedLinks,\n },\n },\n });\n};\n\n/**\n * Analyze the JavaScript bundles for unused locale content and emit the\n * corresponding event. Returns the analysis so callers can report on it.\n */\nexport const checkBundleContent = (\n chunks: BundleChunkInput[],\n html: string,\n currentLocale: string | undefined,\n targetUrl: string,\n totalPageSize: number,\n events: ScanEvent[]\n): BundleContentAnalysis | undefined => {\n if (!currentLocale) {\n events.push({\n type: `url_unusedBundleContent\\\\${targetUrl}`,\n status: 'warning',\n details: {\n warning: 'Cannot analyse bundle content: page locale not detected',\n },\n });\n return undefined;\n }\n\n const analysis = analyzeBundleContent(\n chunks,\n html,\n currentLocale,\n totalPageSize\n );\n\n // Status is driven by the main bundle — lazy chunks with unused content are expected.\n const mainBundleMaxUnused = analysis.mainBundleChunks.reduce(\n (max, c) => Math.max(max, c.unusedPercent),\n 0\n );\n\n const status =\n mainBundleMaxUnused === 0\n ? 'success'\n : mainBundleMaxUnused <= 30\n ? 'warning'\n : 'error';\n\n events.push({\n type: `url_unusedBundleContent\\\\${targetUrl}`,\n status,\n details: { [status]: analysis },\n });\n\n return analysis;\n};\n\n/** Fetch and check `robots.txt`, emitting robots-related events. */\nexport const checkRobots = async (\n origin: string,\n discoveredLocales: Set<string>,\n userAgent: string,\n events: ScanEvent[]\n): Promise<void> => {\n let robotsPresent = false;\n let noLocalizedUrlsForgotten = true;\n const errors: string[] = [];\n\n try {\n const response = await fetch(`${origin}/robots.txt`, {\n headers: { 'User-Agent': userAgent },\n });\n\n if (response.ok) {\n robotsPresent = true;\n const content = await response.text();\n\n if (content && discoveredLocales.size > 0) {\n const disallowedPaths = content\n .split('\\n')\n .map((line) => line.trim().toLowerCase())\n .filter((line) => line.startsWith('disallow:'))\n .map((line) => line.slice('disallow:'.length).trim());\n\n for (const locale of discoveredLocales) {\n for (const path of disallowedPaths) {\n if (path === `/${locale}` || path === `/${locale}/`) {\n noLocalizedUrlsForgotten = false;\n errors.push(\n `Locale path \"${locale}\" appears to be blocked in robots.txt: ${path}`\n );\n }\n }\n }\n }\n }\n } catch (error) {\n errors.push(\n `Failed to fetch robots.txt: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n\n events.push({\n type: 'robots_robotsPresent',\n status: robotsPresent ? 'success' : 'warning',\n details: {\n warning: robotsPresent ? undefined : 'No robots.txt found',\n error: errors.length > 0 ? errors : undefined,\n },\n });\n\n if (robotsPresent) {\n events.push({\n type: 'robots_noLocalizedUrlsForgotten',\n status: noLocalizedUrlsForgotten ? 'success' : 'error',\n details: { error: noLocalizedUrlsForgotten ? undefined : errors },\n });\n }\n};\n\n/** Fetch and check `sitemap.xml`, emitting sitemap-related events. */\nexport const checkSitemap = async (\n origin: string,\n discoveredLocales: Set<string>,\n userAgent: string,\n events: ScanEvent[]\n): Promise<void> => {\n let sitemapPresent = false;\n let hasXDefault = false;\n let hasAlternates = false;\n let noLocalizedUrlsForgotten = true;\n const errors: string[] = [];\n\n try {\n const response = await fetch(`${origin}/sitemap.xml`, {\n headers: { 'User-Agent': userAgent },\n });\n\n if (response.ok) {\n sitemapPresent = true;\n const content = await response.text();\n\n const hreflangMatches = content.match(/hreflang\\s*=\\s*\"([^\"]+)\"/gi) ?? [];\n const hreflangs = hreflangMatches.map((m) =>\n m.replace(/hreflang\\s*=\\s*\"/i, '').replace(/\"$/, '')\n );\n hasAlternates = hreflangs.length > 0;\n hasXDefault = hreflangs.includes('x-default');\n\n if (discoveredLocales.size > 0) {\n const urlBlocks = content.match(/<url\\b[\\s\\S]*?<\\/url>/gi) ?? [];\n const allFoundLocales = new Set<string>();\n let anyUrlMissingLocale = false;\n\n for (const block of urlBlocks) {\n const localesInUrl = new Set<string>();\n\n for (const hreflang of block.match(/hreflang\\s*=\\s*\"([^\"]+)\"/gi) ??\n []) {\n const value = hreflang\n .replace(/hreflang\\s*=\\s*\"/i, '')\n .replace(/\"$/, '');\n if (value !== 'x-default') {\n localesInUrl.add(value);\n allFoundLocales.add(value);\n }\n }\n\n const loc = block.match(/<loc>([\\s\\S]*?)<\\/loc>/i)?.[1]?.trim();\n if (loc) {\n try {\n const firstSegment = new URL(loc).pathname\n .split('/')\n .filter(Boolean)[0];\n if (firstSegment && discoveredLocales.has(firstSegment)) {\n localesInUrl.add(firstSegment);\n allFoundLocales.add(firstSegment);\n }\n } catch {\n /* invalid loc URL, skip */\n }\n }\n\n const missing = [...discoveredLocales].filter(\n (locale) => !localesInUrl.has(locale)\n );\n if (missing.length > 0 && missing.length < discoveredLocales.size) {\n anyUrlMissingLocale = true;\n }\n }\n\n const completelyMissing = [...discoveredLocales].filter(\n (locale) => !allFoundLocales.has(locale)\n );\n if (anyUrlMissingLocale || completelyMissing.length > 0) {\n noLocalizedUrlsForgotten = false;\n if (completelyMissing.length > 0) {\n errors.push(\n `The following locales are completely missing from the sitemap: ${completelyMissing.join(', ')}`\n );\n }\n }\n }\n }\n } catch (error) {\n errors.push(\n `Failed to fetch sitemap.xml: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n\n events.push({\n type: 'sitemap_sitemapPresent',\n status: sitemapPresent ? 'success' : 'warning',\n details: {\n warning: sitemapPresent ? undefined : 'No sitemap.xml found',\n error: errors.length > 0 ? errors : undefined,\n },\n });\n\n if (sitemapPresent) {\n events.push({\n type: 'sitemap_noLocalizedUrlsForgotten',\n status: noLocalizedUrlsForgotten ? 'success' : 'warning',\n details: { warning: noLocalizedUrlsForgotten ? undefined : errors },\n });\n\n events.push({\n type: 'sitemap_hasXDefault',\n status: hasXDefault ? 'success' : 'warning',\n details: {\n warning: hasXDefault ? undefined : 'No x-default hreflang in sitemap',\n },\n });\n\n events.push({\n type: 'sitemap_hasAlternates',\n status: hasAlternates ? 'success' : 'warning',\n details: {\n warning: hasAlternates\n ? undefined\n : 'No alternate language links found in sitemap',\n },\n });\n }\n};\n"],"mappings":"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"}
|
|
@@ -34,7 +34,8 @@ const createFileAdapter = (options) => {
|
|
|
34
34
|
const globPatternLocale = await parseFilePathPattern(source, buildFilePathPatternContext("**", locale));
|
|
35
35
|
const maskPatternLocale = await parseFilePathPattern(source, buildFilePathPatternContext("{{__KEY__}}", locale));
|
|
36
36
|
if (!globPatternLocale || !maskPatternLocale) continue;
|
|
37
|
-
const
|
|
37
|
+
const normalizedGlobPattern = globPatternLocale.startsWith("./") ? globPatternLocale.slice(2) : globPatternLocale;
|
|
38
|
+
const files = await fg(normalizedGlobPattern, { cwd: baseDir });
|
|
38
39
|
const hasLocaleInMask = maskPatternLocale.includes("{{__LOCALE__}}");
|
|
39
40
|
const hasKeyInMask = maskPatternLocale.includes("{{__KEY__}}");
|
|
40
41
|
for (const file of files) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createFileAdapter.mjs","names":[],"sources":["../../../src/syncPluginKit/createFileAdapter.ts"],"sourcesContent":["import { mkdir, readFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, resolve } from 'node:path';\nimport { parseFilePathPattern } from '@intlayer/config/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { FilePathPattern } from '@intlayer/types/filePathPattern';\nimport fg from 'fast-glob';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\nimport { extractKeyAndLocaleFromPath } from './extractKeyAndLocaleFromPath';\nimport { buildFilePathPatternContext } from './filePathPatternHelpers';\nimport type {\n ContentAdapter,\n ContentEntry,\n FormatCodec,\n SyncContent,\n SyncPluginContext,\n} from './types';\n\n/**\n * Discovery strategy of the file adapter:\n * - `strict`: keep only files whose path rebuilds identically from the source\n * pattern, and fabricate entries for missing locales/keys so every declared\n * locale has a write-back target (sync plugins).\n * - `inclusive`: keep every glob match without fabricating missing entries\n * (read-only load plugins, whose patterns may contain free `**` globs).\n */\nexport type FileAdapterDiscovery = 'strict' | 'inclusive';\n\nexport type CreateFileAdapterOptions = {\n /**\n * Location of the files, as a static string, a templated string\n * (e.g. `./messages/{{locale}}/{{key}}.json`) or a function building the\n * path from the key and locale.\n */\n source: FilePathPattern;\n\n /**\n * Format of the files: string payload ↔ structured content.\n */\n codec: FormatCodec;\n\n /**\n * Optional override of how one file is read and parsed. Defaults to a plain\n * utf-8 read followed by `codec.parse`, returning `undefined` for missing or\n * unreadable files. Adapters needing richer loading (e.g. JSON5 or\n * transpiled TS sources) provide their own implementation here.\n */\n readEntry?: (\n absoluteFilePath: string,\n context: SyncPluginContext\n ) => Promise<SyncContent | undefined>;\n\n /**\n * Discovery strategy. Defaults to `'strict'`.\n */\n discovery?: FileAdapterDiscovery;\n};\n\ntype FilePath = string;\ntype MessagesRecord = Record<Locale, Record<string, FilePath>>;\n\n/**\n * Create a filesystem {@link ContentAdapter} for `createSyncPlugin`.\n *\n * Handles glob discovery of the source pattern, key/locale extraction from\n * paths, base-directory resolution, and change-detected atomic writes.\n */\nexport const createFileAdapter = (\n options: CreateFileAdapterOptions\n): ContentAdapter => {\n const { source, codec, discovery = 'strict' } = options;\n const readEntry = options.readEntry ?? createFileReader(codec.parse);\n\n let patternMarkerPromise: Promise<string> | undefined;\n\n /**\n * Source pattern with `{{key}}` / `{{locale}}` markers kept verbatim,\n * memoized because pattern functions can be asynchronous.\n */\n const getPatternMarker = () => {\n patternMarkerPromise ??= parseFilePathPattern(\n source,\n buildFilePathPatternContext('{{key}}', '{{locale}}')\n );\n\n return patternMarkerPromise;\n };\n\n const listMessages = async (\n context: SyncPluginContext\n ): Promise<MessagesRecord> => {\n const { system, internationalization } = context.configuration;\n const { baseDir } = system;\n const { locales } = internationalization;\n\n const result: MessagesRecord = {} as MessagesRecord;\n\n for (const locale of locales) {\n const globPatternLocale = await parseFilePathPattern(\n source,\n buildFilePathPatternContext('**', locale)\n );\n\n const maskPatternLocale = await parseFilePathPattern(\n source,\n buildFilePathPatternContext('{{__KEY__}}', locale)\n );\n\n if (!globPatternLocale || !maskPatternLocale) {\n continue;\n }\n\n const normalizedGlobPattern = globPatternLocale.startsWith('./')\n ? globPatternLocale.slice(2)\n : globPatternLocale;\n\n const files = await fg(normalizedGlobPattern, {\n cwd: baseDir,\n });\n\n const hasLocaleInMask = maskPatternLocale.includes('{{__LOCALE__}}');\n const hasKeyInMask = maskPatternLocale.includes('{{__KEY__}}');\n\n for (const file of files) {\n let key: string;\n let extractedLocale: Locale;\n\n if (hasLocaleInMask || hasKeyInMask) {\n const extraction = extractKeyAndLocaleFromPath(\n file,\n maskPatternLocale,\n locales,\n locale\n );\n\n if (!extraction) {\n continue;\n }\n\n key = extraction.key;\n extractedLocale = extraction.locale;\n } else if (discovery === 'inclusive') {\n // Mask has no placeholders — the file was found via a concrete\n // locale glob. Attribute it directly to the current loop locale.\n key = 'index';\n extractedLocale = locale;\n } else {\n // Strict mode relies on the fabrication step below to attribute\n // placeholder-free sources to their canonical paths.\n continue;\n }\n\n const absoluteFoundPath = isAbsolute(file)\n ? file\n : resolve(baseDir, file);\n\n if (discovery === 'strict') {\n // Rebuild what the path SHOULD be for this key/locale. A mismatch\n // means the file belongs to another plugin/structure.\n const expectedPath = await parseFilePathPattern(\n source,\n buildFilePathPatternContext(key, extractedLocale)\n );\n\n const absoluteExpectedPath = isAbsolute(expectedPath)\n ? expectedPath\n : resolve(baseDir, expectedPath);\n\n if (absoluteFoundPath !== absoluteExpectedPath) {\n continue;\n }\n }\n\n result[extractedLocale] ??= {};\n result[extractedLocale][key] = absoluteFoundPath;\n }\n }\n\n if (discovery === 'inclusive') {\n // Read-only mode only uses actual discovered files; do not fabricate\n // missing locales or keys, since no outputs are written.\n return result;\n }\n\n // Ensure all declared locales are present even if the file doesn't exist yet\n const maskWithKey = await parseFilePathPattern(\n source,\n buildFilePathPatternContext('{{__KEY__}}', locales[0])\n );\n\n const hasKeyInMask = maskWithKey.includes('{{__KEY__}}');\n const discoveredKeys = new Set<string>();\n\n for (const locale of Object.keys(result) as Locale[]) {\n for (const key of Object.keys(result[locale] ?? {})) {\n discoveredKeys.add(key);\n }\n }\n\n if (!hasKeyInMask) {\n discoveredKeys.add('index');\n }\n\n for (const locale of locales) {\n result[locale] ??= {};\n\n for (const key of discoveredKeys) {\n if (!result[locale][key]) {\n const builtPath = await parseFilePathPattern(\n source,\n buildFilePathPatternContext(key, locale)\n );\n\n result[locale][key] = isAbsolute(builtPath)\n ? builtPath\n : resolve(baseDir, builtPath);\n }\n }\n }\n\n return result;\n };\n\n return {\n list: async (context) => {\n const messages = await listMessages(context);\n\n const entries: ContentEntry[] = (\n Object.entries(messages) as [Locale, Record<string, FilePath>][]\n ).flatMap(([locale, keysRecord]) =>\n Object.entries(keysRecord).map(([key, absoluteFilePath]) => ({\n key,\n locale,\n uri: absoluteFilePath,\n filePath: absoluteFilePath,\n }))\n );\n\n return entries;\n },\n\n read: async (entry, context) => await readEntry(entry.uri, context),\n\n write: async ({ key, locale }, content, { configuration }) => {\n const builderPath = await parseFilePathPattern(\n source,\n buildFilePathPatternContext(key, locale)\n );\n\n const absoluteFilePath = resolve(\n configuration.system.baseDir,\n builderPath\n );\n\n await mkdir(dirname(absoluteFilePath), { recursive: true });\n\n await writeFileIfChanged(\n absoluteFilePath,\n codec.serialize(content, { locale }),\n { tempDir: configuration.system?.tempDir }\n );\n },\n\n resolveUri: async ({ key, locale }, { configuration }) => {\n const builderPath = await parseFilePathPattern(\n source,\n buildFilePathPatternContext(key, locale)\n );\n\n return resolve(configuration.system.baseDir, builderPath);\n },\n\n getFillPattern: async () => await getPatternMarker(),\n\n describeSource: async () => await getPatternMarker(),\n\n detectSplitKeys: async () =>\n !(await getPatternMarker()).includes('{{key}}'),\n };\n};\n\n/**\n * Default `readEntry` implementation: plain utf-8 read followed by the given\n * parser. Returns `undefined` for missing or unreadable files.\n */\nexport const createFileReader =\n (parse: (raw: string) => SyncContent) =>\n async (absoluteFilePath: string): Promise<SyncContent | undefined> => {\n try {\n const raw = await readFile(absoluteFilePath, 'utf-8');\n\n return parse(raw);\n } catch {\n return undefined;\n }\n };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createFileAdapter.mjs","names":[],"sources":["../../../src/syncPluginKit/createFileAdapter.ts"],"sourcesContent":["import { mkdir, readFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, resolve } from 'node:path';\nimport { parseFilePathPattern } from '@intlayer/config/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { FilePathPattern } from '@intlayer/types/filePathPattern';\nimport fg from 'fast-glob';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\nimport { extractKeyAndLocaleFromPath } from './extractKeyAndLocaleFromPath';\nimport { buildFilePathPatternContext } from './filePathPatternHelpers';\nimport type {\n ContentAdapter,\n ContentEntry,\n FormatCodec,\n SyncContent,\n SyncPluginContext,\n} from './types';\n\n/**\n * Discovery strategy of the file adapter:\n * - `strict`: keep only files whose path rebuilds identically from the source\n * pattern, and fabricate entries for missing locales/keys so every declared\n * locale has a write-back target (sync plugins).\n * - `inclusive`: keep every glob match without fabricating missing entries\n * (read-only load plugins, whose patterns may contain free `**` globs).\n */\nexport type FileAdapterDiscovery = 'strict' | 'inclusive';\n\nexport type CreateFileAdapterOptions = {\n /**\n * Location of the files, as a static string, a templated string\n * (e.g. `./messages/{{locale}}/{{key}}.json`) or a function building the\n * path from the key and locale.\n */\n source: FilePathPattern;\n\n /**\n * Format of the files: string payload ↔ structured content.\n */\n codec: FormatCodec;\n\n /**\n * Optional override of how one file is read and parsed. Defaults to a plain\n * utf-8 read followed by `codec.parse`, returning `undefined` for missing or\n * unreadable files. Adapters needing richer loading (e.g. JSON5 or\n * transpiled TS sources) provide their own implementation here.\n */\n readEntry?: (\n absoluteFilePath: string,\n context: SyncPluginContext\n ) => Promise<SyncContent | undefined>;\n\n /**\n * Discovery strategy. Defaults to `'strict'`.\n */\n discovery?: FileAdapterDiscovery;\n};\n\ntype FilePath = string;\ntype MessagesRecord = Record<Locale, Record<string, FilePath>>;\n\n/**\n * Create a filesystem {@link ContentAdapter} for `createSyncPlugin`.\n *\n * Handles glob discovery of the source pattern, key/locale extraction from\n * paths, base-directory resolution, and change-detected atomic writes.\n */\nexport const createFileAdapter = (\n options: CreateFileAdapterOptions\n): ContentAdapter => {\n const { source, codec, discovery = 'strict' } = options;\n const readEntry = options.readEntry ?? createFileReader(codec.parse);\n\n let patternMarkerPromise: Promise<string> | undefined;\n\n /**\n * Source pattern with `{{key}}` / `{{locale}}` markers kept verbatim,\n * memoized because pattern functions can be asynchronous.\n */\n const getPatternMarker = () => {\n patternMarkerPromise ??= parseFilePathPattern(\n source,\n buildFilePathPatternContext('{{key}}', '{{locale}}')\n );\n\n return patternMarkerPromise;\n };\n\n const listMessages = async (\n context: SyncPluginContext\n ): Promise<MessagesRecord> => {\n const { system, internationalization } = context.configuration;\n const { baseDir } = system;\n const { locales } = internationalization;\n\n const result: MessagesRecord = {} as MessagesRecord;\n\n for (const locale of locales) {\n const globPatternLocale = await parseFilePathPattern(\n source,\n buildFilePathPatternContext('**', locale)\n );\n\n const maskPatternLocale = await parseFilePathPattern(\n source,\n buildFilePathPatternContext('{{__KEY__}}', locale)\n );\n\n if (!globPatternLocale || !maskPatternLocale) {\n continue;\n }\n\n const normalizedGlobPattern = globPatternLocale.startsWith('./')\n ? globPatternLocale.slice(2)\n : globPatternLocale;\n\n const files = await fg(normalizedGlobPattern, {\n cwd: baseDir,\n });\n\n const hasLocaleInMask = maskPatternLocale.includes('{{__LOCALE__}}');\n const hasKeyInMask = maskPatternLocale.includes('{{__KEY__}}');\n\n for (const file of files) {\n let key: string;\n let extractedLocale: Locale;\n\n if (hasLocaleInMask || hasKeyInMask) {\n const extraction = extractKeyAndLocaleFromPath(\n file,\n maskPatternLocale,\n locales,\n locale\n );\n\n if (!extraction) {\n continue;\n }\n\n key = extraction.key;\n extractedLocale = extraction.locale;\n } else if (discovery === 'inclusive') {\n // Mask has no placeholders — the file was found via a concrete\n // locale glob. Attribute it directly to the current loop locale.\n key = 'index';\n extractedLocale = locale;\n } else {\n // Strict mode relies on the fabrication step below to attribute\n // placeholder-free sources to their canonical paths.\n continue;\n }\n\n const absoluteFoundPath = isAbsolute(file)\n ? file\n : resolve(baseDir, file);\n\n if (discovery === 'strict') {\n // Rebuild what the path SHOULD be for this key/locale. A mismatch\n // means the file belongs to another plugin/structure.\n const expectedPath = await parseFilePathPattern(\n source,\n buildFilePathPatternContext(key, extractedLocale)\n );\n\n const absoluteExpectedPath = isAbsolute(expectedPath)\n ? expectedPath\n : resolve(baseDir, expectedPath);\n\n if (absoluteFoundPath !== absoluteExpectedPath) {\n continue;\n }\n }\n\n result[extractedLocale] ??= {};\n result[extractedLocale][key] = absoluteFoundPath;\n }\n }\n\n if (discovery === 'inclusive') {\n // Read-only mode only uses actual discovered files; do not fabricate\n // missing locales or keys, since no outputs are written.\n return result;\n }\n\n // Ensure all declared locales are present even if the file doesn't exist yet\n const maskWithKey = await parseFilePathPattern(\n source,\n buildFilePathPatternContext('{{__KEY__}}', locales[0])\n );\n\n const hasKeyInMask = maskWithKey.includes('{{__KEY__}}');\n const discoveredKeys = new Set<string>();\n\n for (const locale of Object.keys(result) as Locale[]) {\n for (const key of Object.keys(result[locale] ?? {})) {\n discoveredKeys.add(key);\n }\n }\n\n if (!hasKeyInMask) {\n discoveredKeys.add('index');\n }\n\n for (const locale of locales) {\n result[locale] ??= {};\n\n for (const key of discoveredKeys) {\n if (!result[locale][key]) {\n const builtPath = await parseFilePathPattern(\n source,\n buildFilePathPatternContext(key, locale)\n );\n\n result[locale][key] = isAbsolute(builtPath)\n ? builtPath\n : resolve(baseDir, builtPath);\n }\n }\n }\n\n return result;\n };\n\n return {\n list: async (context) => {\n const messages = await listMessages(context);\n\n const entries: ContentEntry[] = (\n Object.entries(messages) as [Locale, Record<string, FilePath>][]\n ).flatMap(([locale, keysRecord]) =>\n Object.entries(keysRecord).map(([key, absoluteFilePath]) => ({\n key,\n locale,\n uri: absoluteFilePath,\n filePath: absoluteFilePath,\n }))\n );\n\n return entries;\n },\n\n read: async (entry, context) => await readEntry(entry.uri, context),\n\n write: async ({ key, locale }, content, { configuration }) => {\n const builderPath = await parseFilePathPattern(\n source,\n buildFilePathPatternContext(key, locale)\n );\n\n const absoluteFilePath = resolve(\n configuration.system.baseDir,\n builderPath\n );\n\n await mkdir(dirname(absoluteFilePath), { recursive: true });\n\n await writeFileIfChanged(\n absoluteFilePath,\n codec.serialize(content, { locale }),\n { tempDir: configuration.system?.tempDir }\n );\n },\n\n resolveUri: async ({ key, locale }, { configuration }) => {\n const builderPath = await parseFilePathPattern(\n source,\n buildFilePathPatternContext(key, locale)\n );\n\n return resolve(configuration.system.baseDir, builderPath);\n },\n\n getFillPattern: async () => await getPatternMarker(),\n\n describeSource: async () => await getPatternMarker(),\n\n detectSplitKeys: async () =>\n !(await getPatternMarker()).includes('{{key}}'),\n };\n};\n\n/**\n * Default `readEntry` implementation: plain utf-8 read followed by the given\n * parser. Returns `undefined` for missing or unreadable files.\n */\nexport const createFileReader =\n (parse: (raw: string) => SyncContent) =>\n async (absoluteFilePath: string): Promise<SyncContent | undefined> => {\n try {\n const raw = await readFile(absoluteFilePath, 'utf-8');\n\n return parse(raw);\n } catch {\n return undefined;\n }\n };\n"],"mappings":"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"}
|
|
@@ -45,7 +45,10 @@ const createSyncPlugin = (options) => {
|
|
|
45
45
|
const { baseDir } = configuration.system;
|
|
46
46
|
const { defaultLocale } = configuration.internationalization;
|
|
47
47
|
let patternFill;
|
|
48
|
-
if (direction === "both" && adapter.getFillPattern)
|
|
48
|
+
if (direction === "both" && adapter.getFillPattern) {
|
|
49
|
+
const fillPattern = await adapter.getFillPattern(context);
|
|
50
|
+
patternFill = relative(baseDir, resolve(baseDir, fillPattern));
|
|
51
|
+
}
|
|
49
52
|
const dictionaries = [];
|
|
50
53
|
for (const entry of entries) {
|
|
51
54
|
const content = await adapter.read(entry, context) ?? {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createSyncPlugin.mjs","names":[],"sources":["../../../src/syncPluginKit/createSyncPlugin.ts"],"sourcesContent":["import { relative, resolve } from 'node:path';\nimport { colorizePath, getAppLogger } from '@intlayer/config/logger';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { Dictionary, LocalDictionaryId } from '@intlayer/types/dictionary';\nimport type { Plugin } from '@intlayer/types/plugin';\nimport type {\n CreateSyncPluginOptions,\n SyncContent,\n SyncPluginContext,\n} from './types';\n\n/**\n * Content that carries nothing to persist: `undefined`, `null`, or an object\n * without any own key. Writing it back would erase the target.\n */\nconst isEmptyContent = (content: unknown): boolean =>\n typeof content === 'undefined' ||\n content === null ||\n (typeof content === 'object' && Object.keys(content).length === 0);\n\n/**\n * Deep-clone formatted output into plain JSON data, stripping prototypes and\n * non-serializable values before the emptiness check and the write.\n */\nconst toPlainContent = (content: unknown): unknown =>\n typeof content === 'undefined'\n ? undefined\n : JSON.parse(JSON.stringify(content));\n\n/**\n * Build an Intlayer {@link Plugin} from a transport adapter.\n *\n * The factory implements the three plugin hooks once — ingestion\n * (`loadDictionaries`), content-declaration reformatting (`formatOutput`) and\n * write-back (`afterBuild`) — so adapters only describe *where* content lives\n * (filesystem, TMS such as Crowdin, extra CMS…) and codecs only describe the\n * payload format. Write-back only processes dictionaries whose `location`\n * matches this plugin instance, so adapters never see foreign content.\n */\nexport const createSyncPlugin = (options: CreateSyncPluginOptions): Plugin => {\n const {\n name,\n adapter,\n direction = 'both',\n location,\n priority = 0,\n format,\n localeOverride,\n } = options;\n\n let splitKeysPromise: Promise<boolean> | undefined;\n\n /**\n * `splitKeys` resolution is asynchronous (adapter auto-detection may parse\n * an async source pattern), so it is resolved lazily and memoized.\n */\n const resolveSplitKeys = (): Promise<boolean> => {\n splitKeysPromise ??= (async () =>\n options.splitKeys ?? (await adapter.detectSplitKeys?.()) ?? false)();\n\n return splitKeysPromise;\n };\n\n const loadDictionaries: Plugin['loadDictionaries'] = async ({\n configuration,\n }) => {\n const context: SyncPluginContext = { configuration };\n const appLogger = getAppLogger(configuration);\n\n const entries = await adapter.list(context);\n\n if (entries.length === 0) {\n const sourceDescription =\n (await adapter.describeSource?.(context)) ?? name;\n\n appLogger(\n `[${name}] No dictionaries found at locations matching source pattern: ${colorizePath(sourceDescription)}`,\n { level: 'warn' }\n );\n }\n\n const shouldSplitByKeys = await resolveSplitKeys();\n const { baseDir } = configuration.system;\n const { defaultLocale } = configuration.internationalization;\n\n // Sync plugins advertise the source pattern (with {{key}}/{{locale}}\n // markers) as fill target; pull-only plugins fill each entry in place.\n let patternFill: string | undefined;\n\n if (direction === 'both' && adapter.getFillPattern) {\n const fillPattern = await adapter.getFillPattern(context);\n patternFill = relative(baseDir, resolve(baseDir, fillPattern));\n }\n\n const dictionaries: Dictionary[] = [];\n\n for (const entry of entries) {\n const content = (await adapter.read(entry, context)) ?? {};\n\n const relativeFilePath = entry.filePath\n ? relative(baseDir, entry.filePath)\n : undefined;\n\n const usedLocale = (localeOverride ?? entry.locale) as Locale;\n const filled = usedLocale !== defaultLocale ? true : undefined;\n const fill = patternFill ?? relativeFilePath;\n const identifier = relativeFilePath ?? entry.uri;\n\n // One entry groups several namespaces by its first-level keys: emit one\n // dictionary per top-level key (e.g. `Hero`, `Nav`, …).\n if (shouldSplitByKeys) {\n for (const [namespaceKey, namespaceContent] of Object.entries(\n content\n )) {\n dictionaries.push({\n key: namespaceKey,\n locale: usedLocale,\n fill,\n format,\n localId:\n `${namespaceKey}::${location}::${identifier}` as LocalDictionaryId,\n location: location as Dictionary['location'],\n filled,\n content: namespaceContent as SyncContent,\n filePath: relativeFilePath,\n priority,\n } as Dictionary);\n }\n continue;\n }\n\n dictionaries.push({\n key: entry.key,\n locale: usedLocale,\n fill,\n format,\n localId:\n `${entry.key}::${location}::${identifier}` as LocalDictionaryId,\n location: location as Dictionary['location'],\n filled,\n content,\n filePath: relativeFilePath,\n priority,\n } as Dictionary);\n }\n\n return dictionaries;\n };\n\n if (direction === 'pull') {\n return { name, loadDictionaries };\n }\n\n const formatOutput: Plugin['formatOutput'] = async ({\n dictionary,\n configuration,\n }) => {\n if (!dictionary.filePath || !dictionary.locale) return dictionary;\n\n // In split mode several namespaces share the same target; the target is\n // re-assembled in `afterBuild`. Skip here to avoid overwriting the whole\n // target with a single namespace.\n if (await resolveSplitKeys()) return dictionary;\n\n // Ownership check: only reformat declarations this adapter can identify\n // as its own canonical target.\n if (!adapter.resolveUri) return dictionary;\n\n const canonicalUri = await adapter.resolveUri(\n { key: dictionary.key, locale: dictionary.locale as Locale },\n { configuration }\n );\n\n const { baseDir } = configuration.system;\n\n if (\n resolve(baseDir, canonicalUri) !== resolve(baseDir, dictionary.filePath)\n ) {\n return dictionary;\n }\n\n // Lazy import to keep the module graph light when configs are transpiled\n const { formatDictionaryOutput } = await import('../formatDictionary');\n\n return formatDictionaryOutput(dictionary as Dictionary, format).content;\n };\n\n const afterBuild: Plugin['afterBuild'] = async ({\n dictionaries,\n configuration,\n }) => {\n // Lazy imports to keep the module graph light when configs are transpiled\n const { getPerLocaleDictionary } = await import('@intlayer/core/plugins');\n const { formatDictionaryOutput } = await import('../formatDictionary');\n const { parallelize } = await import('../utils/parallelize');\n\n const context: SyncPluginContext = { configuration };\n const { locales } = configuration.internationalization;\n\n // Only ever hand the adapter dictionaries owned by THIS plugin instance.\n const ownedDictionaries = Object.entries(dictionaries.mergedDictionaries)\n .map(([key, result]) => ({\n key,\n dictionary: result.dictionary as Dictionary,\n }))\n .filter(({ dictionary }) => dictionary.location === location);\n\n if (await resolveSplitKeys()) {\n // Split mode: every namespace dictionary writes back into the same\n // per-locale target. Re-assemble them under their top-level key and\n // write each target once, instead of one write per key (which would\n // overwrite).\n const mergedContentByLocale = {} as Record<Locale, SyncContent>;\n const writeKeyByLocale = {} as Record<Locale, string>;\n\n for (const { key, dictionary } of ownedDictionaries) {\n for (const locale of locales) {\n const localizedDictionary = getPerLocaleDictionary(\n dictionary,\n locale\n );\n\n const formattedOutput = formatDictionaryOutput(\n localizedDictionary,\n format\n );\n\n const content = toPlainContent(formattedOutput.content);\n\n if (isEmptyContent(content)) continue;\n\n mergedContentByLocale[locale] ??= {};\n mergedContentByLocale[locale][key] = content;\n writeKeyByLocale[locale] = key;\n }\n }\n\n await parallelize(\n Object.keys(mergedContentByLocale) as Locale[],\n async (locale) => {\n await adapter.write(\n { key: writeKeyByLocale[locale], locale },\n mergedContentByLocale[locale],\n context\n );\n }\n );\n\n return;\n }\n\n const writeTasks = ownedDictionaries.flatMap(({ key, dictionary }) =>\n locales.map((locale) => ({ key, dictionary, locale }))\n );\n\n await parallelize(writeTasks, async ({ key, dictionary, locale }) => {\n const localizedDictionary = getPerLocaleDictionary(dictionary, locale);\n\n const formattedOutput = formatDictionaryOutput(\n localizedDictionary,\n format\n );\n\n const content = toPlainContent(formattedOutput.content);\n\n if (isEmptyContent(content)) return;\n\n await adapter.write({ key, locale }, content as SyncContent, context);\n });\n };\n\n return { name, loadDictionaries, formatOutput, afterBuild };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createSyncPlugin.mjs","names":[],"sources":["../../../src/syncPluginKit/createSyncPlugin.ts"],"sourcesContent":["import { relative, resolve } from 'node:path';\nimport { colorizePath, getAppLogger } from '@intlayer/config/logger';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { Dictionary, LocalDictionaryId } from '@intlayer/types/dictionary';\nimport type { Plugin } from '@intlayer/types/plugin';\nimport type {\n CreateSyncPluginOptions,\n SyncContent,\n SyncPluginContext,\n} from './types';\n\n/**\n * Content that carries nothing to persist: `undefined`, `null`, or an object\n * without any own key. Writing it back would erase the target.\n */\nconst isEmptyContent = (content: unknown): boolean =>\n typeof content === 'undefined' ||\n content === null ||\n (typeof content === 'object' && Object.keys(content).length === 0);\n\n/**\n * Deep-clone formatted output into plain JSON data, stripping prototypes and\n * non-serializable values before the emptiness check and the write.\n */\nconst toPlainContent = (content: unknown): unknown =>\n typeof content === 'undefined'\n ? undefined\n : JSON.parse(JSON.stringify(content));\n\n/**\n * Build an Intlayer {@link Plugin} from a transport adapter.\n *\n * The factory implements the three plugin hooks once — ingestion\n * (`loadDictionaries`), content-declaration reformatting (`formatOutput`) and\n * write-back (`afterBuild`) — so adapters only describe *where* content lives\n * (filesystem, TMS such as Crowdin, extra CMS…) and codecs only describe the\n * payload format. Write-back only processes dictionaries whose `location`\n * matches this plugin instance, so adapters never see foreign content.\n */\nexport const createSyncPlugin = (options: CreateSyncPluginOptions): Plugin => {\n const {\n name,\n adapter,\n direction = 'both',\n location,\n priority = 0,\n format,\n localeOverride,\n } = options;\n\n let splitKeysPromise: Promise<boolean> | undefined;\n\n /**\n * `splitKeys` resolution is asynchronous (adapter auto-detection may parse\n * an async source pattern), so it is resolved lazily and memoized.\n */\n const resolveSplitKeys = (): Promise<boolean> => {\n splitKeysPromise ??= (async () =>\n options.splitKeys ?? (await adapter.detectSplitKeys?.()) ?? false)();\n\n return splitKeysPromise;\n };\n\n const loadDictionaries: Plugin['loadDictionaries'] = async ({\n configuration,\n }) => {\n const context: SyncPluginContext = { configuration };\n const appLogger = getAppLogger(configuration);\n\n const entries = await adapter.list(context);\n\n if (entries.length === 0) {\n const sourceDescription =\n (await adapter.describeSource?.(context)) ?? name;\n\n appLogger(\n `[${name}] No dictionaries found at locations matching source pattern: ${colorizePath(sourceDescription)}`,\n { level: 'warn' }\n );\n }\n\n const shouldSplitByKeys = await resolveSplitKeys();\n const { baseDir } = configuration.system;\n const { defaultLocale } = configuration.internationalization;\n\n // Sync plugins advertise the source pattern (with {{key}}/{{locale}}\n // markers) as fill target; pull-only plugins fill each entry in place.\n let patternFill: string | undefined;\n\n if (direction === 'both' && adapter.getFillPattern) {\n const fillPattern = await adapter.getFillPattern(context);\n patternFill = relative(baseDir, resolve(baseDir, fillPattern));\n }\n\n const dictionaries: Dictionary[] = [];\n\n for (const entry of entries) {\n const content = (await adapter.read(entry, context)) ?? {};\n\n const relativeFilePath = entry.filePath\n ? relative(baseDir, entry.filePath)\n : undefined;\n\n const usedLocale = (localeOverride ?? entry.locale) as Locale;\n const filled = usedLocale !== defaultLocale ? true : undefined;\n const fill = patternFill ?? relativeFilePath;\n const identifier = relativeFilePath ?? entry.uri;\n\n // One entry groups several namespaces by its first-level keys: emit one\n // dictionary per top-level key (e.g. `Hero`, `Nav`, …).\n if (shouldSplitByKeys) {\n for (const [namespaceKey, namespaceContent] of Object.entries(\n content\n )) {\n dictionaries.push({\n key: namespaceKey,\n locale: usedLocale,\n fill,\n format,\n localId:\n `${namespaceKey}::${location}::${identifier}` as LocalDictionaryId,\n location: location as Dictionary['location'],\n filled,\n content: namespaceContent as SyncContent,\n filePath: relativeFilePath,\n priority,\n } as Dictionary);\n }\n continue;\n }\n\n dictionaries.push({\n key: entry.key,\n locale: usedLocale,\n fill,\n format,\n localId:\n `${entry.key}::${location}::${identifier}` as LocalDictionaryId,\n location: location as Dictionary['location'],\n filled,\n content,\n filePath: relativeFilePath,\n priority,\n } as Dictionary);\n }\n\n return dictionaries;\n };\n\n if (direction === 'pull') {\n return { name, loadDictionaries };\n }\n\n const formatOutput: Plugin['formatOutput'] = async ({\n dictionary,\n configuration,\n }) => {\n if (!dictionary.filePath || !dictionary.locale) return dictionary;\n\n // In split mode several namespaces share the same target; the target is\n // re-assembled in `afterBuild`. Skip here to avoid overwriting the whole\n // target with a single namespace.\n if (await resolveSplitKeys()) return dictionary;\n\n // Ownership check: only reformat declarations this adapter can identify\n // as its own canonical target.\n if (!adapter.resolveUri) return dictionary;\n\n const canonicalUri = await adapter.resolveUri(\n { key: dictionary.key, locale: dictionary.locale as Locale },\n { configuration }\n );\n\n const { baseDir } = configuration.system;\n\n if (\n resolve(baseDir, canonicalUri) !== resolve(baseDir, dictionary.filePath)\n ) {\n return dictionary;\n }\n\n // Lazy import to keep the module graph light when configs are transpiled\n const { formatDictionaryOutput } = await import('../formatDictionary');\n\n return formatDictionaryOutput(dictionary as Dictionary, format).content;\n };\n\n const afterBuild: Plugin['afterBuild'] = async ({\n dictionaries,\n configuration,\n }) => {\n // Lazy imports to keep the module graph light when configs are transpiled\n const { getPerLocaleDictionary } = await import('@intlayer/core/plugins');\n const { formatDictionaryOutput } = await import('../formatDictionary');\n const { parallelize } = await import('../utils/parallelize');\n\n const context: SyncPluginContext = { configuration };\n const { locales } = configuration.internationalization;\n\n // Only ever hand the adapter dictionaries owned by THIS plugin instance.\n const ownedDictionaries = Object.entries(dictionaries.mergedDictionaries)\n .map(([key, result]) => ({\n key,\n dictionary: result.dictionary as Dictionary,\n }))\n .filter(({ dictionary }) => dictionary.location === location);\n\n if (await resolveSplitKeys()) {\n // Split mode: every namespace dictionary writes back into the same\n // per-locale target. Re-assemble them under their top-level key and\n // write each target once, instead of one write per key (which would\n // overwrite).\n const mergedContentByLocale = {} as Record<Locale, SyncContent>;\n const writeKeyByLocale = {} as Record<Locale, string>;\n\n for (const { key, dictionary } of ownedDictionaries) {\n for (const locale of locales) {\n const localizedDictionary = getPerLocaleDictionary(\n dictionary,\n locale\n );\n\n const formattedOutput = formatDictionaryOutput(\n localizedDictionary,\n format\n );\n\n const content = toPlainContent(formattedOutput.content);\n\n if (isEmptyContent(content)) continue;\n\n mergedContentByLocale[locale] ??= {};\n mergedContentByLocale[locale][key] = content;\n writeKeyByLocale[locale] = key;\n }\n }\n\n await parallelize(\n Object.keys(mergedContentByLocale) as Locale[],\n async (locale) => {\n await adapter.write(\n { key: writeKeyByLocale[locale], locale },\n mergedContentByLocale[locale],\n context\n );\n }\n );\n\n return;\n }\n\n const writeTasks = ownedDictionaries.flatMap(({ key, dictionary }) =>\n locales.map((locale) => ({ key, dictionary, locale }))\n );\n\n await parallelize(writeTasks, async ({ key, dictionary, locale }) => {\n const localizedDictionary = getPerLocaleDictionary(dictionary, locale);\n\n const formattedOutput = formatDictionaryOutput(\n localizedDictionary,\n format\n );\n\n const content = toPlainContent(formattedOutput.content);\n\n if (isEmptyContent(content)) return;\n\n await adapter.write({ key, locale }, content as SyncContent, context);\n });\n };\n\n return { name, loadDictionaries, formatOutput, afterBuild };\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"babelParser.mjs","names":[],"sources":["../../../src/utils/babelParser.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport type {\n babelTsParser as babelTsParserImpl,\n RecastParser,\n typescriptParser as typescriptParserImpl,\n} from '@intlayer/babel/babelParser';\n\n/**\n * Recast-compatible parsers, backed by `@intlayer/babel`.\n *\n * All Babel operations live in `@intlayer/babel`, so this package never depends\n * on `@babel/parser` directly. `@intlayer/babel/babelParser` is loaded lazily on\n * the first `parse` call: `recast.parse` invokes `parser.parse` synchronously,\n * so a `require` (rather than a dynamic `import`) is used to keep the API\n * synchronous while deferring the load — `@babel/parser` stays off the hot path\n * until an AST transform actually runs.\n *\n * `@intlayer/babel` is an optional peer dependency of `@intlayer/engine`. The\n * flows that use these parsers (`intlayer init`, content-declaration writes) are\n * driven by `@intlayer/cli`, which ships `@intlayer/babel`, so it is present\n * whenever these parsers are needed.\n */\n\n/** Works in both the ESM and CJS builds (see `@intlayer/config`'s helper). */\nconst isESModule = typeof import.meta.url === 'string';\nconst engineRequire: NodeJS.Require = isESModule\n ? createRequire(import.meta.url)\n : require;\n\nconst BABEL_PARSER_MODULE = '@intlayer/babel/babelParser';\n\ntype BabelParserModule = {\n typescriptParser: typeof typescriptParserImpl;\n babelTsParser: typeof babelTsParserImpl;\n};\n\nlet cachedModule: BabelParserModule | undefined;\n\n/**\n * Lazily resolves `@intlayer/babel/babelParser`, caching the result.\n *\n * @throws If `@intlayer/babel` is not installed.\n */\nconst loadBabelParser = (): BabelParserModule => {\n if (cachedModule) return cachedModule;\n\n try {\n cachedModule = engineRequire(BABEL_PARSER_MODULE) as BabelParserModule;\n } catch (error) {\n throw new Error(\n `Failed to load \"${BABEL_PARSER_MODULE}\". Install \"@intlayer/babel\" to enable AST-based transforms.`,\n { cause: error }\n );\n }\n\n return cachedModule;\n};\n\nexport type { RecastParser };\n\n/** Drop-in replacement for `recast/parsers/typescript` (TS without JSX). */\nexport const typescriptParser: RecastParser = {\n parse: (source: string) => loadBabelParser().typescriptParser.parse(source),\n};\n\n/** Drop-in replacement for `recast/parsers/babel-ts` (TS *and* JSX). */\nexport const babelTsParser: RecastParser = {\n parse: (source: string) => loadBabelParser().babelTsParser.parse(source),\n};\n"],"mappings":";;;;AAyBA,MAAM,gBADa,OAAO,
|
|
1
|
+
{"version":3,"file":"babelParser.mjs","names":[],"sources":["../../../src/utils/babelParser.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport type {\n babelTsParser as babelTsParserImpl,\n RecastParser,\n typescriptParser as typescriptParserImpl,\n} from '@intlayer/babel/babelParser';\n\n/**\n * Recast-compatible parsers, backed by `@intlayer/babel`.\n *\n * All Babel operations live in `@intlayer/babel`, so this package never depends\n * on `@babel/parser` directly. `@intlayer/babel/babelParser` is loaded lazily on\n * the first `parse` call: `recast.parse` invokes `parser.parse` synchronously,\n * so a `require` (rather than a dynamic `import`) is used to keep the API\n * synchronous while deferring the load — `@babel/parser` stays off the hot path\n * until an AST transform actually runs.\n *\n * `@intlayer/babel` is an optional peer dependency of `@intlayer/engine`. The\n * flows that use these parsers (`intlayer init`, content-declaration writes) are\n * driven by `@intlayer/cli`, which ships `@intlayer/babel`, so it is present\n * whenever these parsers are needed.\n */\n\n/** Works in both the ESM and CJS builds (see `@intlayer/config`'s helper). */\nconst isESModule = typeof import.meta.url === 'string';\nconst engineRequire: NodeJS.Require = isESModule\n ? createRequire(import.meta.url)\n : require;\n\nconst BABEL_PARSER_MODULE = '@intlayer/babel/babelParser';\n\ntype BabelParserModule = {\n typescriptParser: typeof typescriptParserImpl;\n babelTsParser: typeof babelTsParserImpl;\n};\n\nlet cachedModule: BabelParserModule | undefined;\n\n/**\n * Lazily resolves `@intlayer/babel/babelParser`, caching the result.\n *\n * @throws If `@intlayer/babel` is not installed.\n */\nconst loadBabelParser = (): BabelParserModule => {\n if (cachedModule) return cachedModule;\n\n try {\n cachedModule = engineRequire(BABEL_PARSER_MODULE) as BabelParserModule;\n } catch (error) {\n throw new Error(\n `Failed to load \"${BABEL_PARSER_MODULE}\". Install \"@intlayer/babel\" to enable AST-based transforms.`,\n { cause: error }\n );\n }\n\n return cachedModule;\n};\n\nexport type { RecastParser };\n\n/** Drop-in replacement for `recast/parsers/typescript` (TS without JSX). */\nexport const typescriptParser: RecastParser = {\n parse: (source: string) => loadBabelParser().typescriptParser.parse(source),\n};\n\n/** Drop-in replacement for `recast/parsers/babel-ts` (TS *and* JSX). */\nexport const babelTsParser: RecastParser = {\n parse: (source: string) => loadBabelParser().babelTsParser.parse(source),\n};\n"],"mappings":";;;;AAyBA,MAAM,gBADa,OAAO,YAAY,QAAQ,WAE1C,cAAc,YAAY,GAAG;AAGjC,MAAM,sBAAsB;AAO5B,IAAI;;;;;;AAOJ,MAAM,wBAA2C;CAC/C,IAAI,cAAc,OAAO;CAEzB,IAAI;EACF,eAAe,cAAc,mBAAmB;CAClD,SAAS,OAAO;EACd,MAAM,IAAI,MACR,mBAAmB,oBAAoB,+DACvC,EAAE,OAAO,MAAM,CACjB;CACF;CAEA,OAAO;AACT;;AAKA,MAAa,mBAAiC,EAC5C,QAAQ,WAAmB,gBAAgB,CAAC,CAAC,iBAAiB,MAAM,MAAM,EAC5E;;AAGA,MAAa,gBAA8B,EACzC,QAAQ,WAAmB,gBAAgB,CAAC,CAAC,cAAc,MAAM,MAAM,EACzE"}
|
|
@@ -16,9 +16,7 @@ const getContentExtension = (format, configuration) => {
|
|
|
16
16
|
case "cjs":
|
|
17
17
|
contentFileExtension = ".cjs";
|
|
18
18
|
break;
|
|
19
|
-
case "esm":
|
|
20
|
-
contentFileExtension = ".js";
|
|
21
|
-
break;
|
|
19
|
+
case "esm": contentFileExtension = ".js";
|
|
22
20
|
}
|
|
23
21
|
return configuration.content.fileExtensions.find((extension) => extension.endsWith(contentFileExtension)) ?? configuration.content.fileExtensions[0] ?? `.content${contentFileExtension}`;
|
|
24
22
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getContentExtension.mjs","names":[],"sources":["../../../src/utils/getContentExtension.ts"],"sourcesContent":["import type { IntlayerConfig } from '@intlayer/types/config';\n\n/**\n * Generates the .content file extension based on the desired format\n * and the project's fileExtensions configuration.\n *\n * Example:\n * format 'ts' -> '.content.ts'\n * format 'esm' -> '.content.js' (if matching .content.js is found in config)\n */\nexport const getContentExtension = (\n format: 'ts' | 'esm' | 'cjs' | 'json' | 'jsonc' | 'json5' | (string & {}),\n configuration: IntlayerConfig\n): string => {\n let contentFileExtension = '.ts';\n switch (format) {\n case 'json':\n contentFileExtension = '.json';\n break;\n case 'cjs':\n contentFileExtension = '.cjs';\n break;\n case 'esm':\n contentFileExtension = '.js';\n break;\n }\n\n // Find the configured extension that matches the format's extension\n const pickedFileExtension = configuration.content.fileExtensions.find(\n (extension) => extension.endsWith(contentFileExtension)\n );\n\n return (\n pickedFileExtension ??\n configuration.content.fileExtensions[0] ??\n `.content${contentFileExtension}`\n );\n};\n"],"mappings":";;;;;;;;;AAUA,MAAa,uBACX,QACA,kBACW;CACX,IAAI,uBAAuB;CAC3B,QAAQ,QAAR;EACE,KAAK;GACH,uBAAuB;GACvB;EACF,KAAK;GACH,uBAAuB;GACvB;EACF,KAAK
|
|
1
|
+
{"version":3,"file":"getContentExtension.mjs","names":[],"sources":["../../../src/utils/getContentExtension.ts"],"sourcesContent":["import type { IntlayerConfig } from '@intlayer/types/config';\n\n/**\n * Generates the .content file extension based on the desired format\n * and the project's fileExtensions configuration.\n *\n * Example:\n * format 'ts' -> '.content.ts'\n * format 'esm' -> '.content.js' (if matching .content.js is found in config)\n */\nexport const getContentExtension = (\n format: 'ts' | 'esm' | 'cjs' | 'json' | 'jsonc' | 'json5' | (string & {}),\n configuration: IntlayerConfig\n): string => {\n let contentFileExtension = '.ts';\n switch (format) {\n case 'json':\n contentFileExtension = '.json';\n break;\n case 'cjs':\n contentFileExtension = '.cjs';\n break;\n case 'esm':\n contentFileExtension = '.js';\n break;\n }\n\n // Find the configured extension that matches the format's extension\n const pickedFileExtension = configuration.content.fileExtensions.find(\n (extension) => extension.endsWith(contentFileExtension)\n );\n\n return (\n pickedFileExtension ??\n configuration.content.fileExtensions[0] ??\n `.content${contentFileExtension}`\n );\n};\n"],"mappings":";;;;;;;;;AAUA,MAAa,uBACX,QACA,kBACW;CACX,IAAI,uBAAuB;CAC3B,QAAQ,QAAR;EACE,KAAK;GACH,uBAAuB;GACvB;EACF,KAAK;GACH,uBAAuB;GACvB;EACF,KAAK,OACH,uBAAuB;CAE3B;CAOA,OAJ4B,cAAc,QAAQ,eAAe,MAC9D,cAAc,UAAU,SAAS,oBAAoB,CAIpC,KAClB,cAAc,QAAQ,eAAe,MACrC,WAAW;AAEf"}
|
|
@@ -11,7 +11,10 @@ const resolveRelativePath = (path, sourcePath, baseDir) => {
|
|
|
11
11
|
}
|
|
12
12
|
return normalizedResult;
|
|
13
13
|
}
|
|
14
|
-
if (path.startsWith("./") || path.startsWith("../"))
|
|
14
|
+
if (path.startsWith("./") || path.startsWith("../")) {
|
|
15
|
+
const fileDir = dirname(sourcePath);
|
|
16
|
+
return resolve(fileDir, path);
|
|
17
|
+
}
|
|
15
18
|
return resolve(baseDir, path);
|
|
16
19
|
};
|
|
17
20
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveRelativePath.mjs","names":[],"sources":["../../../src/utils/resolveRelativePath.ts"],"sourcesContent":["import { dirname, isAbsolute, normalize, resolve } from 'node:path';\n\nexport const resolveRelativePath = (\n path: string,\n sourcePath: string,\n baseDir: string\n) => {\n // Handle absolute paths\n if (isAbsolute(path)) {\n const normalizedResult = normalize(path);\n const normalizedBaseDir = normalize(baseDir);\n\n // Check if it's relative to baseDir (starts with /)\n // but not a system path (like /usr/local)\n if (\n path.startsWith('/') &&\n !normalizedResult.startsWith(normalizedBaseDir)\n ) {\n // Try to resolve it relative to baseDir first\n const relativeToBase = resolve(baseDir, path.substring(1));\n\n // If the path doesn't exist in common system directories, treat as relative to baseDir\n if (\n !path.startsWith('/usr/') &&\n !path.startsWith('/etc/') &&\n !path.startsWith('/var/') &&\n !path.startsWith('/home/') &&\n !path.startsWith('/Users/') &&\n !path.startsWith('/tmp/') &&\n !path.startsWith('/private/') &&\n !path.startsWith('/opt/')\n ) {\n return relativeToBase;\n }\n }\n\n // It's a true system absolute path\n return normalizedResult;\n }\n\n // Handle relative paths (starting with ./ or ../)\n if (path.startsWith('./') || path.startsWith('../')) {\n const fileDir = dirname(sourcePath);\n return resolve(fileDir, path);\n }\n\n // Default case: treat as relative to baseDir\n return resolve(baseDir, path);\n};\n"],"mappings":";;;AAEA,MAAa,uBACX,MACA,YACA,YACG;CAEH,IAAI,WAAW,IAAI,GAAG;EACpB,MAAM,mBAAmB,UAAU,IAAI;EACvC,MAAM,oBAAoB,UAAU,OAAO;EAI3C,IACE,KAAK,WAAW,GAAG,KACnB,CAAC,iBAAiB,WAAW,iBAAiB,GAC9C;GAEA,MAAM,iBAAiB,QAAQ,SAAS,KAAK,UAAU,CAAC,CAAC;GAGzD,IACE,CAAC,KAAK,WAAW,OAAO,KACxB,CAAC,KAAK,WAAW,OAAO,KACxB,CAAC,KAAK,WAAW,OAAO,KACxB,CAAC,KAAK,WAAW,QAAQ,KACzB,CAAC,KAAK,WAAW,SAAS,KAC1B,CAAC,KAAK,WAAW,OAAO,KACxB,CAAC,KAAK,WAAW,WAAW,KAC5B,CAAC,KAAK,WAAW,OAAO,GAExB,OAAO;EAEX;EAGA,OAAO;CACT;CAGA,IAAI,KAAK,WAAW,IAAI,KAAK,KAAK,WAAW,KAAK,
|
|
1
|
+
{"version":3,"file":"resolveRelativePath.mjs","names":[],"sources":["../../../src/utils/resolveRelativePath.ts"],"sourcesContent":["import { dirname, isAbsolute, normalize, resolve } from 'node:path';\n\nexport const resolveRelativePath = (\n path: string,\n sourcePath: string,\n baseDir: string\n) => {\n // Handle absolute paths\n if (isAbsolute(path)) {\n const normalizedResult = normalize(path);\n const normalizedBaseDir = normalize(baseDir);\n\n // Check if it's relative to baseDir (starts with /)\n // but not a system path (like /usr/local)\n if (\n path.startsWith('/') &&\n !normalizedResult.startsWith(normalizedBaseDir)\n ) {\n // Try to resolve it relative to baseDir first\n const relativeToBase = resolve(baseDir, path.substring(1));\n\n // If the path doesn't exist in common system directories, treat as relative to baseDir\n if (\n !path.startsWith('/usr/') &&\n !path.startsWith('/etc/') &&\n !path.startsWith('/var/') &&\n !path.startsWith('/home/') &&\n !path.startsWith('/Users/') &&\n !path.startsWith('/tmp/') &&\n !path.startsWith('/private/') &&\n !path.startsWith('/opt/')\n ) {\n return relativeToBase;\n }\n }\n\n // It's a true system absolute path\n return normalizedResult;\n }\n\n // Handle relative paths (starting with ./ or ../)\n if (path.startsWith('./') || path.startsWith('../')) {\n const fileDir = dirname(sourcePath);\n return resolve(fileDir, path);\n }\n\n // Default case: treat as relative to baseDir\n return resolve(baseDir, path);\n};\n"],"mappings":";;;AAEA,MAAa,uBACX,MACA,YACA,YACG;CAEH,IAAI,WAAW,IAAI,GAAG;EACpB,MAAM,mBAAmB,UAAU,IAAI;EACvC,MAAM,oBAAoB,UAAU,OAAO;EAI3C,IACE,KAAK,WAAW,GAAG,KACnB,CAAC,iBAAiB,WAAW,iBAAiB,GAC9C;GAEA,MAAM,iBAAiB,QAAQ,SAAS,KAAK,UAAU,CAAC,CAAC;GAGzD,IACE,CAAC,KAAK,WAAW,OAAO,KACxB,CAAC,KAAK,WAAW,OAAO,KACxB,CAAC,KAAK,WAAW,OAAO,KACxB,CAAC,KAAK,WAAW,QAAQ,KACzB,CAAC,KAAK,WAAW,SAAS,KAC1B,CAAC,KAAK,WAAW,OAAO,KACxB,CAAC,KAAK,WAAW,WAAW,KAC5B,CAAC,KAAK,WAAW,OAAO,GAExB,OAAO;EAEX;EAGA,OAAO;CACT;CAGA,IAAI,KAAK,WAAW,IAAI,KAAK,KAAK,WAAW,KAAK,GAAG;EACnD,MAAM,UAAU,QAAQ,UAAU;EAClC,OAAO,QAAQ,SAAS,IAAI;CAC9B;CAGA,OAAO,QAAQ,SAAS,IAAI;AAC9B"}
|
|
@@ -5,8 +5,8 @@ import packageJson from "@intlayer/core/package.json" with { type: "json" };
|
|
|
5
5
|
|
|
6
6
|
//#region src/utils/runOnce.ts
|
|
7
7
|
const DEFAULT_RUN_ONCE_OPTIONS = {
|
|
8
|
-
cacheTimeoutMs:
|
|
9
|
-
lockWaitTimeoutMs:
|
|
8
|
+
cacheTimeoutMs: 6e4,
|
|
9
|
+
lockWaitTimeoutMs: 3e5
|
|
10
10
|
};
|
|
11
11
|
/** Delay between two reads of a sentinel owned by another process. */
|
|
12
12
|
const LOCK_POLL_INTERVAL_MS = 50;
|