@platforma-sdk/tengo-builder 4.0.28 → 4.1.0
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 +30 -0
- package/dist/cli.cjs +2 -0
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/build.cjs +1 -14
- package/dist/commands/build.cjs.map +1 -1
- package/dist/commands/build.js +1 -13
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/imports.cjs +79 -0
- package/dist/commands/imports.cjs.map +1 -0
- package/dist/commands/imports.js +76 -0
- package/dist/commands/imports.js.map +1 -0
- package/dist/compiler/imports.cjs +68 -0
- package/dist/compiler/imports.cjs.map +1 -0
- package/dist/compiler/imports.js +67 -0
- package/dist/compiler/imports.js.map +1 -0
- package/dist/compiler/source.cjs +6 -3
- package/dist/compiler/source.cjs.map +1 -1
- package/dist/compiler/source.js +6 -3
- package/dist/compiler/source.js.map +1 -1
- package/dist/compiler/util.cjs +19 -0
- package/dist/compiler/util.cjs.map +1 -1
- package/dist/compiler/util.js +20 -2
- package/dist/compiler/util.js.map +1 -1
- package/package.json +3 -3
- package/src/cli.ts +2 -0
- package/src/commands/build.ts +1 -25
- package/src/commands/imports.ts +110 -0
- package/src/compiler/imports.test.ts +181 -0
- package/src/compiler/imports.ts +96 -0
- package/src/compiler/source.ts +13 -4
- package/src/compiler/util.ts +30 -0
package/dist/compiler/source.cjs
CHANGED
|
@@ -181,7 +181,8 @@ function processModuleImport(importInstruction, originalLine, statement, context
|
|
|
181
181
|
line: originalLine,
|
|
182
182
|
context,
|
|
183
183
|
artifacts: [],
|
|
184
|
-
option: void 0
|
|
184
|
+
option: void 0,
|
|
185
|
+
moduleImport: iInfo
|
|
185
186
|
};
|
|
186
187
|
}
|
|
187
188
|
if (iInfo.module === "@milaboratory/tengo-sdk:ll" || iInfo.module === "@platforma-sdk/workflow-tengo:ll" || (localPackageName === "@milaboratory/tengo-sdk" || localPackageName === "@platforma-sdk/workflow-tengo") && iInfo.module === ":ll") {
|
|
@@ -208,14 +209,16 @@ function processModuleImport(importInstruction, originalLine, statement, context
|
|
|
208
209
|
line: originalLine,
|
|
209
210
|
context,
|
|
210
211
|
artifacts: [],
|
|
211
|
-
option: void 0
|
|
212
|
+
option: void 0,
|
|
213
|
+
moduleImport: iInfo
|
|
212
214
|
};
|
|
213
215
|
if (globalizeImports) originalLine = originalLine.replace(importInstruction[0], ` := import("${artifact.pkg}:${artifact.id}")`);
|
|
214
216
|
return {
|
|
215
217
|
line: originalLine,
|
|
216
218
|
context,
|
|
217
219
|
artifacts: [artifact],
|
|
218
|
-
option: void 0
|
|
220
|
+
option: void 0,
|
|
221
|
+
moduleImport: iInfo
|
|
219
222
|
};
|
|
220
223
|
}
|
|
221
224
|
function processAssetImport(originalLine, statement, context, localPackageName, globalizeImports) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source.cjs","names":["createArtifactNameSet","fullNameToString"],"sources":["../../src/compiler/source.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport {\n type TypedArtifactName,\n type FullArtifactName,\n type ArtifactType,\n type CompileMode,\n type CompilerOption,\n fullNameToString,\n} from \"./package\";\nimport type { ArtifactMap } from \"./artifactset\";\nimport { createArtifactNameSet } from \"./artifactset\";\nimport { createHash } from \"node:crypto\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\n\n// matches any valid name in tengo. Don't forget to use '\\b' when needed to limit the boundaries!\nconst namePattern = \"[_a-zA-Z][_a-zA-Z0-9]*\";\n\nconst functionCallRE = (moduleName: string, fnName: string) => {\n // `fnCall` captures from the function name through the first string\n // argument (including the closing quote and any trailing whitespace);\n // the closing-paren-or-comma check is a non-consuming lookahead, so\n // any extra args (`assets.importWasm(\"name\", { memoryLimit: ... })`)\n // survive the rewrite in `compiler.ts` — only the literal is\n // build-time material, anything past it is runtime config.\n return new RegExp(\n `\\\\b${moduleName}\\\\.(?<fnCall>(?<fnName>` +\n fnName +\n `)\\\\s*\\\\(\\\\s*\"(?<templateName>[^\"]+)\"\\\\s*)(?=[,)])`,\n \"g\",\n );\n};\n\nconst functionCallLikeRE = (moduleName: string, fnName: string) => {\n return new RegExp(`\\\\b${moduleName}\\\\.(?<fnName>` + fnName + `)\\\\s*\\\\(`, \"g\");\n};\n\nexport const newGetTemplateIdRE = (moduleName: string) =>\n functionCallRE(moduleName, \"getTemplateId\");\nexport const newGetSoftwareInfoRE = (moduleName: string) =>\n functionCallRE(moduleName, \"getSoftwareInfo\");\n\nconst newImportTemplateRE = (moduleName: string) => functionCallRE(moduleName, \"importTemplate\");\nconst newImportTemplateDetector = (moduleName: string) =>\n functionCallLikeRE(moduleName, \"importTemplate\");\nconst newImportSoftwareRE = (moduleName: string) => functionCallRE(moduleName, \"importSoftware\");\nconst newImportSoftwareDetector = (moduleName: string) =>\n functionCallLikeRE(moduleName, \"importSoftware\");\nconst newImportAssetRE = (moduleName: string) => functionCallRE(moduleName, \"importAsset\");\nconst newImportAssetDetector = (moduleName: string) =>\n functionCallLikeRE(moduleName, \"importAsset\");\nconst newImportWasmRE = (moduleName: string) => functionCallRE(moduleName, \"importWasm\");\nconst newImportWasmDetector = (moduleName: string) => functionCallLikeRE(moduleName, \"importWasm\");\n\nconst emptyLineRE = /^\\s*$/;\nconst compilerOptionRE = /^\\/\\/tengo:[\\w]/;\nconst wrongCompilerOptionRE = /^\\s*\\/\\/\\s*tengo:\\s*./;\nconst singlelineCommentRE = /^\\s*(\\/\\/)/;\nconst singlelineTerminatedCommentRE = /^\\s*\\/\\*.*\\*\\/\\s*$/; // matches '^/* ... */$' comment lines as a special case of singleline comments.\nconst multilineCommentStartRE = /^\\s*\\/\\*/;\nconst multilineCommentEndRE = /\\*\\//;\nconst multilineStatementRE = /[.,]\\s*$/; // it is hard to consistently treat (\\n\"a\"\\n) multiline statements, we forbid them for now.\n\n// import could only be an assignment in a statement,\n// other ways could break a compilation.\nconst importRE = /\\s*:=\\s*import\\s*\\(\\s*\"(?<moduleName>[^\"]+)\"\\s*\\)/;\nconst importNameRE = new RegExp(\n `\\\\b(?<importName>${namePattern}(\\\\.${namePattern})*)${importRE.source}`,\n);\nconst dependencyRE = /(?<pkgName>[^\"]+)?:(?<depID>[^\"]+)/; // use it to parse <moduleName> from importPattern or <templateName> from getTemplateID\n\n/**\n * Parse compiler option string representation\n * Compiler option line is a comment starting with '//tengo:', say\n * //tengo:hash_override tralala\n *\n * The common compiler option syntax is:\n * //tengo:<option name> [<option arg1> [<option arg 2> [...]]]\n */\nconst parseComplierOption = (opt: string): CompilerOption => {\n const parts = opt.split(\" \");\n const namePart = parts[0].split(\":\");\n if (namePart.length != 2) {\n throw new Error(\n \"compiler option format is wrong: expect to have option name after 'tengo:' prefix, like 'tengo:MyOption'\",\n );\n }\n const optName = namePart[1];\n\n return {\n name: optName,\n args: parts.slice(1),\n };\n};\n\nexport class ArtifactSource {\n constructor(\n /** The mode this artifact was built (dev or dist) */\n public readonly compileMode: CompileMode,\n /** Full artifact id, including package version */\n public readonly fullName: FullArtifactName,\n /** Hash of the source code */\n public readonly sourceHash: string,\n /** Normalized source code */\n public readonly src: string,\n /** Path to source file where artifact came from */\n public readonly srcName: string,\n /** List of dependencies */\n public readonly dependencies: TypedArtifactName[],\n /** Additional compiler options detected in source code */\n public readonly compilerOptions: CompilerOption[],\n ) {}\n}\n\nexport function parseSourceFile(\n logger: MiLogger,\n mode: CompileMode,\n srcFile: string,\n fullSourceName: FullArtifactName,\n normalize: boolean,\n): ArtifactSource {\n const src = readFileSync(srcFile).toString();\n const { deps, normalized, opts } = parseSourceData(logger, src, fullSourceName, normalize);\n\n return new ArtifactSource(\n mode,\n fullSourceName,\n getSha256(normalized),\n normalized,\n srcFile,\n deps.array,\n opts,\n );\n}\n\nexport function parseSource(\n logger: MiLogger,\n mode: CompileMode,\n src: string,\n fullSourceName: FullArtifactName,\n normalize: boolean,\n): ArtifactSource {\n const { deps, normalized, opts } = parseSourceData(logger, src, fullSourceName, normalize);\n\n return new ArtifactSource(\n mode,\n fullSourceName,\n getSha256(normalized),\n normalized,\n \"\",\n deps.array,\n opts,\n );\n}\n\n/**\n * Reads src\n * returns normalized source code,\n * gets dependencies from imports,\n * maps imports to global names if globalizeImports is true,\n * and collects compiler options like hashOverride.\n */\nfunction parseSourceData(\n logger: MiLogger,\n src: string,\n fullSourceName: FullArtifactName,\n globalizeImports: boolean,\n): {\n normalized: string;\n deps: ArtifactMap<TypedArtifactName>;\n opts: CompilerOption[];\n} {\n const dependencySet = createArtifactNameSet();\n const optionList: CompilerOption[] = [];\n\n // iterating over lines\n const lines = src.split(\"\\n\");\n\n // processedLines keep all the original lines from <src>.\n // If <globalizeImport>==true, the parser modifies 'import' and 'getTemplateId' lines\n // with Platforma Tengo lib and template usages, resolving local names (\":<item>\") to\n // global (\"@milaboratory/pkg:<item>\")\n const processedLines: string[] = [];\n let parserContext: sourceParserContext = {\n isInCommentBlock: false,\n canDetectOptions: true,\n artifactImportREs: new Map<string, [ArtifactType, RegExp][]>(),\n importLikeREs: new Map<string, [ArtifactType, RegExp][]>(),\n multilineStatement: \"\",\n lineNo: 0,\n };\n\n for (const line of lines) {\n parserContext.lineNo++;\n\n try {\n const {\n line: processedLine,\n context: newContext,\n artifacts,\n option,\n } = parseSingleSourceLine(logger, line, parserContext, fullSourceName.pkg, globalizeImports);\n processedLines.push(processedLine);\n parserContext = newContext;\n\n for (const artifact of artifacts ?? []) {\n dependencySet.add(artifact);\n }\n if (option) {\n optionList.push(option);\n }\n } catch (error: unknown) {\n const err = error as Error;\n throw new Error(\n `[line ${parserContext.lineNo} in ${fullNameToString(fullSourceName)}]: ${err.message}\\n\\t${line}`,\n { cause: err },\n );\n }\n }\n\n return {\n normalized: processedLines.join(\"\\n\"),\n deps: dependencySet,\n opts: optionList,\n };\n}\n\nexport type sourceParserContext = {\n isInCommentBlock: boolean;\n canDetectOptions: boolean;\n artifactImportREs: Map<string, [ArtifactType, RegExp][]>;\n importLikeREs: Map<string, [ArtifactType, RegExp][]>;\n multilineStatement: string;\n lineNo: number;\n};\n\nexport type lineProcessingResult = {\n line: string;\n context: sourceParserContext;\n artifacts: TypedArtifactName[];\n option: CompilerOption | undefined;\n};\n\nexport function parseSingleSourceLine(\n logger: MiLogger,\n line: string,\n context: sourceParserContext,\n localPackageName: string,\n globalizeImports?: boolean,\n): lineProcessingResult {\n if (context.isInCommentBlock) {\n if (multilineCommentEndRE.exec(line)) {\n context.isInCommentBlock = false;\n }\n return { line: \"\", context, artifacts: [], option: undefined };\n }\n\n if (compilerOptionRE.exec(line)) {\n if (!context.canDetectOptions) {\n logger.error(\n `[line ${context.lineNo}]: compiler option '//tengo:' was detected, but it cannot be applied as compiler options can be set only at the file header, before any code line'`,\n );\n throw new Error(\n \"tengo compiler options ('//tengo:' comments) can be set only in file header\",\n );\n }\n return { line, context, artifacts: [], option: parseComplierOption(line) };\n }\n\n if (wrongCompilerOptionRE.exec(line) && context.canDetectOptions) {\n logger.warn(\n `[line ${context.lineNo}]: text simillar to compiler option ('//tengo:...') was detected, but it has wrong format. Leave it as is, if you did not mean to use a line as compiler option. Or format it to '//tengo:<option>' otherwise (no spaces between '//' and 'tengo', no spaces between ':' and option name)`,\n );\n return { line, context, artifacts: [], option: undefined };\n }\n\n if (singlelineCommentRE.test(line) || singlelineTerminatedCommentRE.test(line)) {\n return { line: \"\", context, artifacts: [], option: undefined };\n }\n\n const canBeInlinedComment = line.includes(\"*/\");\n if (multilineCommentStartRE.exec(line) && !canBeInlinedComment) {\n context.isInCommentBlock = true;\n return { line: \"\", context, artifacts: [], option: undefined };\n }\n\n const statement = context.multilineStatement + line.trim();\n\n const mayContainAComment = line.includes(\"//\") || line.includes(\"/*\");\n if (multilineStatementRE.test(line) && !mayContainAComment) {\n // We accumulate multiline statements into single line before analyzing them.\n // This dramatically simplifies parsing logic: things like\n //\n // assets.\n // importSoftware(\"a:b\");\n //\n // become simple 'assets.importSoftware(\"a:b\");' for parser checks.\n //\n // For safety reasons, we never consider anything that 'may look like a comment'\n // as a part of multiline statement to prevent joining things like\n //\n // someFnCall() // looks like multiline statement because of dot in the end of a comment.\n //\n // This problem also appears in multiline string literals, but I hope this will not\n // cause problems in real life.\n\n // We still try to process each line to globalize imports in case of complex constructions, when\n // statements are stacked one into another:\n // a.\n // use(assets.importSoftware(\":soft1\")).\n // use(assets.importSoftware(\":soft2\")).\n // run()\n // It is multiline, and it still requires import globalization mid-way, not just for the last line of statement\n const result = processAssetImport(line, statement, context, localPackageName, globalizeImports);\n context.multilineStatement += result.line.trim(); // accumulate the line after imports globalization.\n return result;\n }\n\n context.multilineStatement = \"\"; // reset accumulated multiline statement parts once we reach statement end.\n\n if (emptyLineRE.exec(statement)) {\n return { line, context, artifacts: [], option: undefined };\n }\n\n // options could be only at the top of the file.\n context.canDetectOptions = false;\n\n return processAssetImport(line, statement, context, localPackageName, globalizeImports);\n}\n\nfunction processModuleImport(\n importInstruction: RegExpExecArray,\n originalLine: string,\n statement: string,\n context: sourceParserContext,\n localPackageName: string,\n globalizeImports?: boolean,\n): lineProcessingResult {\n const iInfo = parseImport(statement);\n\n // If we have plapi, ll or assets, then try to parse\n // getTemplateId, getSoftwareInfo, getSoftware and getAsset calls.\n\n if (iInfo.module === \"plapi\") {\n if (!context.artifactImportREs.has(iInfo.module)) {\n context.artifactImportREs.set(iInfo.module, [\n [\"template\", newGetTemplateIdRE(iInfo.alias)],\n [\"software\", newGetSoftwareInfoRE(iInfo.alias)],\n ]);\n }\n return { line: originalLine, context, artifacts: [], option: undefined };\n }\n\n if (\n iInfo.module === \"@milaboratory/tengo-sdk:ll\" ||\n iInfo.module === \"@platforma-sdk/workflow-tengo:ll\" ||\n ((localPackageName === \"@milaboratory/tengo-sdk\" ||\n localPackageName === \"@platforma-sdk/workflow-tengo\") &&\n iInfo.module === \":ll\")\n ) {\n if (!context.artifactImportREs.has(iInfo.module)) {\n context.artifactImportREs.set(iInfo.module, [\n [\"template\", newImportTemplateRE(iInfo.alias)],\n [\"software\", newImportSoftwareRE(iInfo.alias)],\n ]);\n }\n }\n\n if (\n iInfo.module === \"@milaboratory/tengo-sdk:assets\" ||\n iInfo.module === \"@platforma-sdk/workflow-tengo:assets\" ||\n ((localPackageName === \"@milaboratory/tengo-sdk\" ||\n localPackageName === \"@platforma-sdk/workflow-tengo\") &&\n iInfo.module === \":assets\")\n ) {\n if (!context.artifactImportREs.has(iInfo.module)) {\n context.artifactImportREs.set(iInfo.module, [\n [\"template\", newImportTemplateRE(iInfo.alias)],\n [\"software\", newImportSoftwareRE(iInfo.alias)],\n [\"asset\", newImportAssetRE(iInfo.alias)],\n [\"wasm\", newImportWasmRE(iInfo.alias)],\n ]);\n context.importLikeREs.set(iInfo.module, [\n [\"template\", newImportTemplateDetector(iInfo.alias)],\n [\"software\", newImportSoftwareDetector(iInfo.alias)],\n [\"asset\", newImportAssetDetector(iInfo.alias)],\n [\"wasm\", newImportWasmDetector(iInfo.alias)],\n ]);\n }\n }\n\n const artifact = parseArtifactName(iInfo.module, \"library\", localPackageName);\n if (!artifact) {\n // not a Platforma Tengo library import\n return { line: originalLine, context, artifacts: [], option: undefined };\n }\n\n if (globalizeImports) {\n originalLine = originalLine.replace(\n importInstruction[0],\n ` := import(\"${artifact.pkg}:${artifact.id}\")`,\n );\n }\n\n return { line: originalLine, context, artifacts: [artifact], option: undefined };\n}\n\nfunction processAssetImport(\n originalLine: string,\n statement: string,\n context: sourceParserContext,\n localPackageName: string,\n globalizeImports?: boolean,\n): lineProcessingResult {\n if (emptyLineRE.exec(statement)) {\n return { line: originalLine, context, artifacts: [], option: undefined };\n }\n\n // options could be only at the top of the file.\n context.canDetectOptions = false;\n\n const importInstruction = importRE.exec(statement);\n\n if (importInstruction) {\n return processModuleImport(\n importInstruction,\n originalLine,\n statement,\n context,\n localPackageName,\n globalizeImports,\n );\n }\n\n if (context.artifactImportREs.size > 0) {\n for (const [_, artifactRE] of context.artifactImportREs) {\n for (const [artifactType, re] of artifactRE) {\n // Find all matches in the statement\n const matches = Array.from(statement.matchAll(re));\n if (matches.length === 0) {\n continue;\n }\n\n const artifacts: TypedArtifactName[] = [];\n for (let i = matches.length - 1; i >= 0; i--) {\n const match = matches[i];\n if (!match || !match.groups) {\n continue;\n }\n\n const { fnCall, templateName, fnName } = match.groups;\n\n if (!fnCall || !templateName || !fnName) {\n throw Error(`failed to parse template import statement`);\n }\n\n const artifact = parseArtifactName(templateName, artifactType, localPackageName);\n if (!artifact) {\n throw Error(`failed to parse artifact name in ${fnName} import statement`);\n }\n artifacts.push(artifact);\n\n if (globalizeImports) {\n // Replace all occurrences of this fnCall in originalLine.\n // fnCall ends right after the literal's closing quote + ws;\n // the trailing `)` or `,` is preserved from the original\n // line, so multi-arg calls (`fn(\"name\", opts)`) keep their\n // remaining args intact.\n originalLine = originalLine.replaceAll(\n fnCall,\n `${fnName}(\"${artifact.pkg}:${artifact.id}\"`,\n );\n }\n }\n\n return { line: originalLine, context, artifacts, option: undefined };\n }\n }\n }\n\n if (context.importLikeREs.size > 0) {\n for (const [_, artifactRE] of context.importLikeREs) {\n for (const [artifactType, re] of artifactRE) {\n const match = re.exec(statement);\n if (!match || !match.groups) {\n continue;\n }\n\n throw Error(\n `incorrect '${artifactType}' import statement: use string literal as ID (variables are not allowed) in the same line with brackets (i.e. 'importSoftware(\"sw:main\")').`,\n );\n }\n }\n }\n\n return { line: originalLine, context, artifacts: [], option: undefined };\n}\n\ninterface ImportInfo {\n module: string; // the module name without wrapping quotes: import(\"<module>\")\n alias: string; // the name of variable that keeps imported module: <alias> := import(\"<module>\")\n}\n\nfunction parseImport(line: string): ImportInfo {\n const match = importNameRE.exec(line);\n\n if (!match || !match.groups) {\n throw Error(`failed to parse 'import' statement`);\n }\n\n const { importName, moduleName } = match.groups;\n if (!importName || !moduleName) {\n throw Error(`failed to parse 'import' statement`);\n }\n\n return {\n module: moduleName, // the module name without wrapping quotes: import(\"<module>\")\n alias: importName, // the name of variable that keeps imported module: <alias> := import(\"<module>\")\n };\n}\n\nfunction parseArtifactName(\n moduleName: string,\n aType: ArtifactType,\n localPackageName: string,\n): TypedArtifactName | undefined {\n const depInfo = dependencyRE.exec(moduleName);\n if (!depInfo) {\n return;\n }\n\n if (!depInfo.groups) {\n throw Error(\n `failed to parse dependency name inside 'import' statement. The dependency name should have format '<package>:<templateName>'`,\n );\n }\n\n const { pkgName, depID } = depInfo.groups;\n if (!depID) {\n throw Error(\n `failed to parse dependency name inside 'import' statement. The dependency name should have format '<package>:<templateName>'`,\n );\n }\n\n return { type: aType, pkg: pkgName ?? localPackageName, id: depID };\n}\n\nexport function getSha256(source: string): string {\n return createHash(\"sha256\").update(source).digest(\"hex\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"source.cjs","names":["createArtifactNameSet","fullNameToString"],"sources":["../../src/compiler/source.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport {\n type TypedArtifactName,\n type FullArtifactName,\n type ArtifactType,\n type CompileMode,\n type CompilerOption,\n fullNameToString,\n} from \"./package\";\nimport type { ArtifactMap } from \"./artifactset\";\nimport { createArtifactNameSet } from \"./artifactset\";\nimport { createHash } from \"node:crypto\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\n\n// matches any valid name in tengo. Don't forget to use '\\b' when needed to limit the boundaries!\nconst namePattern = \"[_a-zA-Z][_a-zA-Z0-9]*\";\n\nconst functionCallRE = (moduleName: string, fnName: string) => {\n // `fnCall` captures from the function name through the first string\n // argument (including the closing quote and any trailing whitespace);\n // the closing-paren-or-comma check is a non-consuming lookahead, so\n // any extra args (`assets.importWasm(\"name\", { memoryLimit: ... })`)\n // survive the rewrite in `compiler.ts` — only the literal is\n // build-time material, anything past it is runtime config.\n return new RegExp(\n `\\\\b${moduleName}\\\\.(?<fnCall>(?<fnName>` +\n fnName +\n `)\\\\s*\\\\(\\\\s*\"(?<templateName>[^\"]+)\"\\\\s*)(?=[,)])`,\n \"g\",\n );\n};\n\nconst functionCallLikeRE = (moduleName: string, fnName: string) => {\n return new RegExp(`\\\\b${moduleName}\\\\.(?<fnName>` + fnName + `)\\\\s*\\\\(`, \"g\");\n};\n\nexport const newGetTemplateIdRE = (moduleName: string) =>\n functionCallRE(moduleName, \"getTemplateId\");\nexport const newGetSoftwareInfoRE = (moduleName: string) =>\n functionCallRE(moduleName, \"getSoftwareInfo\");\n\nconst newImportTemplateRE = (moduleName: string) => functionCallRE(moduleName, \"importTemplate\");\nconst newImportTemplateDetector = (moduleName: string) =>\n functionCallLikeRE(moduleName, \"importTemplate\");\nconst newImportSoftwareRE = (moduleName: string) => functionCallRE(moduleName, \"importSoftware\");\nconst newImportSoftwareDetector = (moduleName: string) =>\n functionCallLikeRE(moduleName, \"importSoftware\");\nconst newImportAssetRE = (moduleName: string) => functionCallRE(moduleName, \"importAsset\");\nconst newImportAssetDetector = (moduleName: string) =>\n functionCallLikeRE(moduleName, \"importAsset\");\nconst newImportWasmRE = (moduleName: string) => functionCallRE(moduleName, \"importWasm\");\nconst newImportWasmDetector = (moduleName: string) => functionCallLikeRE(moduleName, \"importWasm\");\n\nconst emptyLineRE = /^\\s*$/;\nconst compilerOptionRE = /^\\/\\/tengo:[\\w]/;\nconst wrongCompilerOptionRE = /^\\s*\\/\\/\\s*tengo:\\s*./;\nconst singlelineCommentRE = /^\\s*(\\/\\/)/;\nconst singlelineTerminatedCommentRE = /^\\s*\\/\\*.*\\*\\/\\s*$/; // matches '^/* ... */$' comment lines as a special case of singleline comments.\nconst multilineCommentStartRE = /^\\s*\\/\\*/;\nconst multilineCommentEndRE = /\\*\\//;\nconst multilineStatementRE = /[.,]\\s*$/; // it is hard to consistently treat (\\n\"a\"\\n) multiline statements, we forbid them for now.\n\n// import could only be an assignment in a statement,\n// other ways could break a compilation.\nconst importRE = /\\s*:=\\s*import\\s*\\(\\s*\"(?<moduleName>[^\"]+)\"\\s*\\)/;\nconst importNameRE = new RegExp(\n `\\\\b(?<importName>${namePattern}(\\\\.${namePattern})*)${importRE.source}`,\n);\nconst dependencyRE = /(?<pkgName>[^\"]+)?:(?<depID>[^\"]+)/; // use it to parse <moduleName> from importPattern or <templateName> from getTemplateID\n\n/**\n * Parse compiler option string representation\n * Compiler option line is a comment starting with '//tengo:', say\n * //tengo:hash_override tralala\n *\n * The common compiler option syntax is:\n * //tengo:<option name> [<option arg1> [<option arg 2> [...]]]\n */\nconst parseComplierOption = (opt: string): CompilerOption => {\n const parts = opt.split(\" \");\n const namePart = parts[0].split(\":\");\n if (namePart.length != 2) {\n throw new Error(\n \"compiler option format is wrong: expect to have option name after 'tengo:' prefix, like 'tengo:MyOption'\",\n );\n }\n const optName = namePart[1];\n\n return {\n name: optName,\n args: parts.slice(1),\n };\n};\n\nexport class ArtifactSource {\n constructor(\n /** The mode this artifact was built (dev or dist) */\n public readonly compileMode: CompileMode,\n /** Full artifact id, including package version */\n public readonly fullName: FullArtifactName,\n /** Hash of the source code */\n public readonly sourceHash: string,\n /** Normalized source code */\n public readonly src: string,\n /** Path to source file where artifact came from */\n public readonly srcName: string,\n /** List of dependencies */\n public readonly dependencies: TypedArtifactName[],\n /** Additional compiler options detected in source code */\n public readonly compilerOptions: CompilerOption[],\n ) {}\n}\n\nexport function parseSourceFile(\n logger: MiLogger,\n mode: CompileMode,\n srcFile: string,\n fullSourceName: FullArtifactName,\n normalize: boolean,\n): ArtifactSource {\n const src = readFileSync(srcFile).toString();\n const { deps, normalized, opts } = parseSourceData(logger, src, fullSourceName, normalize);\n\n return new ArtifactSource(\n mode,\n fullSourceName,\n getSha256(normalized),\n normalized,\n srcFile,\n deps.array,\n opts,\n );\n}\n\nexport function parseSource(\n logger: MiLogger,\n mode: CompileMode,\n src: string,\n fullSourceName: FullArtifactName,\n normalize: boolean,\n): ArtifactSource {\n const { deps, normalized, opts } = parseSourceData(logger, src, fullSourceName, normalize);\n\n return new ArtifactSource(\n mode,\n fullSourceName,\n getSha256(normalized),\n normalized,\n \"\",\n deps.array,\n opts,\n );\n}\n\n/**\n * Reads src\n * returns normalized source code,\n * gets dependencies from imports,\n * maps imports to global names if globalizeImports is true,\n * and collects compiler options like hashOverride.\n */\nfunction parseSourceData(\n logger: MiLogger,\n src: string,\n fullSourceName: FullArtifactName,\n globalizeImports: boolean,\n): {\n normalized: string;\n deps: ArtifactMap<TypedArtifactName>;\n opts: CompilerOption[];\n} {\n const dependencySet = createArtifactNameSet();\n const optionList: CompilerOption[] = [];\n\n // iterating over lines\n const lines = src.split(\"\\n\");\n\n // processedLines keep all the original lines from <src>.\n // If <globalizeImport>==true, the parser modifies 'import' and 'getTemplateId' lines\n // with Platforma Tengo lib and template usages, resolving local names (\":<item>\") to\n // global (\"@milaboratory/pkg:<item>\")\n const processedLines: string[] = [];\n let parserContext: sourceParserContext = {\n isInCommentBlock: false,\n canDetectOptions: true,\n artifactImportREs: new Map<string, [ArtifactType, RegExp][]>(),\n importLikeREs: new Map<string, [ArtifactType, RegExp][]>(),\n multilineStatement: \"\",\n lineNo: 0,\n };\n\n for (const line of lines) {\n parserContext.lineNo++;\n\n try {\n const {\n line: processedLine,\n context: newContext,\n artifacts,\n option,\n } = parseSingleSourceLine(logger, line, parserContext, fullSourceName.pkg, globalizeImports);\n processedLines.push(processedLine);\n parserContext = newContext;\n\n for (const artifact of artifacts ?? []) {\n dependencySet.add(artifact);\n }\n if (option) {\n optionList.push(option);\n }\n } catch (error: unknown) {\n const err = error as Error;\n throw new Error(\n `[line ${parserContext.lineNo} in ${fullNameToString(fullSourceName)}]: ${err.message}\\n\\t${line}`,\n { cause: err },\n );\n }\n }\n\n return {\n normalized: processedLines.join(\"\\n\"),\n deps: dependencySet,\n opts: optionList,\n };\n}\n\nexport type sourceParserContext = {\n isInCommentBlock: boolean;\n canDetectOptions: boolean;\n artifactImportREs: Map<string, [ArtifactType, RegExp][]>;\n importLikeREs: Map<string, [ArtifactType, RegExp][]>;\n multilineStatement: string;\n lineNo: number;\n};\n\nexport type lineProcessingResult = {\n line: string;\n context: sourceParserContext;\n artifacts: TypedArtifactName[];\n option: CompilerOption | undefined;\n /** Set when the line is a module import, for every module: Platforma\n * artifacts, `plapi` and the Tengo standard library alike. */\n moduleImport?: ImportInfo;\n};\n\nexport function parseSingleSourceLine(\n logger: MiLogger,\n line: string,\n context: sourceParserContext,\n localPackageName: string,\n globalizeImports?: boolean,\n): lineProcessingResult {\n if (context.isInCommentBlock) {\n if (multilineCommentEndRE.exec(line)) {\n context.isInCommentBlock = false;\n }\n return { line: \"\", context, artifacts: [], option: undefined };\n }\n\n if (compilerOptionRE.exec(line)) {\n if (!context.canDetectOptions) {\n logger.error(\n `[line ${context.lineNo}]: compiler option '//tengo:' was detected, but it cannot be applied as compiler options can be set only at the file header, before any code line'`,\n );\n throw new Error(\n \"tengo compiler options ('//tengo:' comments) can be set only in file header\",\n );\n }\n return { line, context, artifacts: [], option: parseComplierOption(line) };\n }\n\n if (wrongCompilerOptionRE.exec(line) && context.canDetectOptions) {\n logger.warn(\n `[line ${context.lineNo}]: text simillar to compiler option ('//tengo:...') was detected, but it has wrong format. Leave it as is, if you did not mean to use a line as compiler option. Or format it to '//tengo:<option>' otherwise (no spaces between '//' and 'tengo', no spaces between ':' and option name)`,\n );\n return { line, context, artifacts: [], option: undefined };\n }\n\n if (singlelineCommentRE.test(line) || singlelineTerminatedCommentRE.test(line)) {\n return { line: \"\", context, artifacts: [], option: undefined };\n }\n\n const canBeInlinedComment = line.includes(\"*/\");\n if (multilineCommentStartRE.exec(line) && !canBeInlinedComment) {\n context.isInCommentBlock = true;\n return { line: \"\", context, artifacts: [], option: undefined };\n }\n\n const statement = context.multilineStatement + line.trim();\n\n const mayContainAComment = line.includes(\"//\") || line.includes(\"/*\");\n if (multilineStatementRE.test(line) && !mayContainAComment) {\n // We accumulate multiline statements into single line before analyzing them.\n // This dramatically simplifies parsing logic: things like\n //\n // assets.\n // importSoftware(\"a:b\");\n //\n // become simple 'assets.importSoftware(\"a:b\");' for parser checks.\n //\n // For safety reasons, we never consider anything that 'may look like a comment'\n // as a part of multiline statement to prevent joining things like\n //\n // someFnCall() // looks like multiline statement because of dot in the end of a comment.\n //\n // This problem also appears in multiline string literals, but I hope this will not\n // cause problems in real life.\n\n // We still try to process each line to globalize imports in case of complex constructions, when\n // statements are stacked one into another:\n // a.\n // use(assets.importSoftware(\":soft1\")).\n // use(assets.importSoftware(\":soft2\")).\n // run()\n // It is multiline, and it still requires import globalization mid-way, not just for the last line of statement\n const result = processAssetImport(line, statement, context, localPackageName, globalizeImports);\n context.multilineStatement += result.line.trim(); // accumulate the line after imports globalization.\n return result;\n }\n\n context.multilineStatement = \"\"; // reset accumulated multiline statement parts once we reach statement end.\n\n if (emptyLineRE.exec(statement)) {\n return { line, context, artifacts: [], option: undefined };\n }\n\n // options could be only at the top of the file.\n context.canDetectOptions = false;\n\n return processAssetImport(line, statement, context, localPackageName, globalizeImports);\n}\n\nfunction processModuleImport(\n importInstruction: RegExpExecArray,\n originalLine: string,\n statement: string,\n context: sourceParserContext,\n localPackageName: string,\n globalizeImports?: boolean,\n): lineProcessingResult {\n const iInfo = parseImport(statement);\n\n // If we have plapi, ll or assets, then try to parse\n // getTemplateId, getSoftwareInfo, getSoftware and getAsset calls.\n\n if (iInfo.module === \"plapi\") {\n if (!context.artifactImportREs.has(iInfo.module)) {\n context.artifactImportREs.set(iInfo.module, [\n [\"template\", newGetTemplateIdRE(iInfo.alias)],\n [\"software\", newGetSoftwareInfoRE(iInfo.alias)],\n ]);\n }\n return { line: originalLine, context, artifacts: [], option: undefined, moduleImport: iInfo };\n }\n\n if (\n iInfo.module === \"@milaboratory/tengo-sdk:ll\" ||\n iInfo.module === \"@platforma-sdk/workflow-tengo:ll\" ||\n ((localPackageName === \"@milaboratory/tengo-sdk\" ||\n localPackageName === \"@platforma-sdk/workflow-tengo\") &&\n iInfo.module === \":ll\")\n ) {\n if (!context.artifactImportREs.has(iInfo.module)) {\n context.artifactImportREs.set(iInfo.module, [\n [\"template\", newImportTemplateRE(iInfo.alias)],\n [\"software\", newImportSoftwareRE(iInfo.alias)],\n ]);\n }\n }\n\n if (\n iInfo.module === \"@milaboratory/tengo-sdk:assets\" ||\n iInfo.module === \"@platforma-sdk/workflow-tengo:assets\" ||\n ((localPackageName === \"@milaboratory/tengo-sdk\" ||\n localPackageName === \"@platforma-sdk/workflow-tengo\") &&\n iInfo.module === \":assets\")\n ) {\n if (!context.artifactImportREs.has(iInfo.module)) {\n context.artifactImportREs.set(iInfo.module, [\n [\"template\", newImportTemplateRE(iInfo.alias)],\n [\"software\", newImportSoftwareRE(iInfo.alias)],\n [\"asset\", newImportAssetRE(iInfo.alias)],\n [\"wasm\", newImportWasmRE(iInfo.alias)],\n ]);\n context.importLikeREs.set(iInfo.module, [\n [\"template\", newImportTemplateDetector(iInfo.alias)],\n [\"software\", newImportSoftwareDetector(iInfo.alias)],\n [\"asset\", newImportAssetDetector(iInfo.alias)],\n [\"wasm\", newImportWasmDetector(iInfo.alias)],\n ]);\n }\n }\n\n const artifact = parseArtifactName(iInfo.module, \"library\", localPackageName);\n if (!artifact) {\n // not a Platforma Tengo library import\n return { line: originalLine, context, artifacts: [], option: undefined, moduleImport: iInfo };\n }\n\n if (globalizeImports) {\n originalLine = originalLine.replace(\n importInstruction[0],\n ` := import(\"${artifact.pkg}:${artifact.id}\")`,\n );\n }\n\n return {\n line: originalLine,\n context,\n artifacts: [artifact],\n option: undefined,\n moduleImport: iInfo,\n };\n}\n\nfunction processAssetImport(\n originalLine: string,\n statement: string,\n context: sourceParserContext,\n localPackageName: string,\n globalizeImports?: boolean,\n): lineProcessingResult {\n if (emptyLineRE.exec(statement)) {\n return { line: originalLine, context, artifacts: [], option: undefined };\n }\n\n // options could be only at the top of the file.\n context.canDetectOptions = false;\n\n const importInstruction = importRE.exec(statement);\n\n if (importInstruction) {\n return processModuleImport(\n importInstruction,\n originalLine,\n statement,\n context,\n localPackageName,\n globalizeImports,\n );\n }\n\n if (context.artifactImportREs.size > 0) {\n for (const [_, artifactRE] of context.artifactImportREs) {\n for (const [artifactType, re] of artifactRE) {\n // Find all matches in the statement\n const matches = Array.from(statement.matchAll(re));\n if (matches.length === 0) {\n continue;\n }\n\n const artifacts: TypedArtifactName[] = [];\n for (let i = matches.length - 1; i >= 0; i--) {\n const match = matches[i];\n if (!match || !match.groups) {\n continue;\n }\n\n const { fnCall, templateName, fnName } = match.groups;\n\n if (!fnCall || !templateName || !fnName) {\n throw Error(`failed to parse template import statement`);\n }\n\n const artifact = parseArtifactName(templateName, artifactType, localPackageName);\n if (!artifact) {\n throw Error(`failed to parse artifact name in ${fnName} import statement`);\n }\n artifacts.push(artifact);\n\n if (globalizeImports) {\n // Replace all occurrences of this fnCall in originalLine.\n // fnCall ends right after the literal's closing quote + ws;\n // the trailing `)` or `,` is preserved from the original\n // line, so multi-arg calls (`fn(\"name\", opts)`) keep their\n // remaining args intact.\n originalLine = originalLine.replaceAll(\n fnCall,\n `${fnName}(\"${artifact.pkg}:${artifact.id}\"`,\n );\n }\n }\n\n return { line: originalLine, context, artifacts, option: undefined };\n }\n }\n }\n\n if (context.importLikeREs.size > 0) {\n for (const [_, artifactRE] of context.importLikeREs) {\n for (const [artifactType, re] of artifactRE) {\n const match = re.exec(statement);\n if (!match || !match.groups) {\n continue;\n }\n\n throw Error(\n `incorrect '${artifactType}' import statement: use string literal as ID (variables are not allowed) in the same line with brackets (i.e. 'importSoftware(\"sw:main\")').`,\n );\n }\n }\n }\n\n return { line: originalLine, context, artifacts: [], option: undefined };\n}\n\nexport interface ImportInfo {\n module: string; // the module name without wrapping quotes: import(\"<module>\")\n alias: string; // the name of variable that keeps imported module: <alias> := import(\"<module>\")\n}\n\nfunction parseImport(line: string): ImportInfo {\n const match = importNameRE.exec(line);\n\n if (!match || !match.groups) {\n throw Error(`failed to parse 'import' statement`);\n }\n\n const { importName, moduleName } = match.groups;\n if (!importName || !moduleName) {\n throw Error(`failed to parse 'import' statement`);\n }\n\n return {\n module: moduleName, // the module name without wrapping quotes: import(\"<module>\")\n alias: importName, // the name of variable that keeps imported module: <alias> := import(\"<module>\")\n };\n}\n\nfunction parseArtifactName(\n moduleName: string,\n aType: ArtifactType,\n localPackageName: string,\n): TypedArtifactName | undefined {\n const depInfo = dependencyRE.exec(moduleName);\n if (!depInfo) {\n return;\n }\n\n if (!depInfo.groups) {\n throw Error(\n `failed to parse dependency name inside 'import' statement. The dependency name should have format '<package>:<templateName>'`,\n );\n }\n\n const { pkgName, depID } = depInfo.groups;\n if (!depID) {\n throw Error(\n `failed to parse dependency name inside 'import' statement. The dependency name should have format '<package>:<templateName>'`,\n );\n }\n\n return { type: aType, pkg: pkgName ?? localPackageName, id: depID };\n}\n\nexport function getSha256(source: string): string {\n return createHash(\"sha256\").update(source).digest(\"hex\");\n}\n"],"mappings":"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"}
|
package/dist/compiler/source.js
CHANGED
|
@@ -181,7 +181,8 @@ function processModuleImport(importInstruction, originalLine, statement, context
|
|
|
181
181
|
line: originalLine,
|
|
182
182
|
context,
|
|
183
183
|
artifacts: [],
|
|
184
|
-
option: void 0
|
|
184
|
+
option: void 0,
|
|
185
|
+
moduleImport: iInfo
|
|
185
186
|
};
|
|
186
187
|
}
|
|
187
188
|
if (iInfo.module === "@milaboratory/tengo-sdk:ll" || iInfo.module === "@platforma-sdk/workflow-tengo:ll" || (localPackageName === "@milaboratory/tengo-sdk" || localPackageName === "@platforma-sdk/workflow-tengo") && iInfo.module === ":ll") {
|
|
@@ -208,14 +209,16 @@ function processModuleImport(importInstruction, originalLine, statement, context
|
|
|
208
209
|
line: originalLine,
|
|
209
210
|
context,
|
|
210
211
|
artifacts: [],
|
|
211
|
-
option: void 0
|
|
212
|
+
option: void 0,
|
|
213
|
+
moduleImport: iInfo
|
|
212
214
|
};
|
|
213
215
|
if (globalizeImports) originalLine = originalLine.replace(importInstruction[0], ` := import("${artifact.pkg}:${artifact.id}")`);
|
|
214
216
|
return {
|
|
215
217
|
line: originalLine,
|
|
216
218
|
context,
|
|
217
219
|
artifacts: [artifact],
|
|
218
|
-
option: void 0
|
|
220
|
+
option: void 0,
|
|
221
|
+
moduleImport: iInfo
|
|
219
222
|
};
|
|
220
223
|
}
|
|
221
224
|
function processAssetImport(originalLine, statement, context, localPackageName, globalizeImports) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source.js","names":[],"sources":["../../src/compiler/source.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport {\n type TypedArtifactName,\n type FullArtifactName,\n type ArtifactType,\n type CompileMode,\n type CompilerOption,\n fullNameToString,\n} from \"./package\";\nimport type { ArtifactMap } from \"./artifactset\";\nimport { createArtifactNameSet } from \"./artifactset\";\nimport { createHash } from \"node:crypto\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\n\n// matches any valid name in tengo. Don't forget to use '\\b' when needed to limit the boundaries!\nconst namePattern = \"[_a-zA-Z][_a-zA-Z0-9]*\";\n\nconst functionCallRE = (moduleName: string, fnName: string) => {\n // `fnCall` captures from the function name through the first string\n // argument (including the closing quote and any trailing whitespace);\n // the closing-paren-or-comma check is a non-consuming lookahead, so\n // any extra args (`assets.importWasm(\"name\", { memoryLimit: ... })`)\n // survive the rewrite in `compiler.ts` — only the literal is\n // build-time material, anything past it is runtime config.\n return new RegExp(\n `\\\\b${moduleName}\\\\.(?<fnCall>(?<fnName>` +\n fnName +\n `)\\\\s*\\\\(\\\\s*\"(?<templateName>[^\"]+)\"\\\\s*)(?=[,)])`,\n \"g\",\n );\n};\n\nconst functionCallLikeRE = (moduleName: string, fnName: string) => {\n return new RegExp(`\\\\b${moduleName}\\\\.(?<fnName>` + fnName + `)\\\\s*\\\\(`, \"g\");\n};\n\nexport const newGetTemplateIdRE = (moduleName: string) =>\n functionCallRE(moduleName, \"getTemplateId\");\nexport const newGetSoftwareInfoRE = (moduleName: string) =>\n functionCallRE(moduleName, \"getSoftwareInfo\");\n\nconst newImportTemplateRE = (moduleName: string) => functionCallRE(moduleName, \"importTemplate\");\nconst newImportTemplateDetector = (moduleName: string) =>\n functionCallLikeRE(moduleName, \"importTemplate\");\nconst newImportSoftwareRE = (moduleName: string) => functionCallRE(moduleName, \"importSoftware\");\nconst newImportSoftwareDetector = (moduleName: string) =>\n functionCallLikeRE(moduleName, \"importSoftware\");\nconst newImportAssetRE = (moduleName: string) => functionCallRE(moduleName, \"importAsset\");\nconst newImportAssetDetector = (moduleName: string) =>\n functionCallLikeRE(moduleName, \"importAsset\");\nconst newImportWasmRE = (moduleName: string) => functionCallRE(moduleName, \"importWasm\");\nconst newImportWasmDetector = (moduleName: string) => functionCallLikeRE(moduleName, \"importWasm\");\n\nconst emptyLineRE = /^\\s*$/;\nconst compilerOptionRE = /^\\/\\/tengo:[\\w]/;\nconst wrongCompilerOptionRE = /^\\s*\\/\\/\\s*tengo:\\s*./;\nconst singlelineCommentRE = /^\\s*(\\/\\/)/;\nconst singlelineTerminatedCommentRE = /^\\s*\\/\\*.*\\*\\/\\s*$/; // matches '^/* ... */$' comment lines as a special case of singleline comments.\nconst multilineCommentStartRE = /^\\s*\\/\\*/;\nconst multilineCommentEndRE = /\\*\\//;\nconst multilineStatementRE = /[.,]\\s*$/; // it is hard to consistently treat (\\n\"a\"\\n) multiline statements, we forbid them for now.\n\n// import could only be an assignment in a statement,\n// other ways could break a compilation.\nconst importRE = /\\s*:=\\s*import\\s*\\(\\s*\"(?<moduleName>[^\"]+)\"\\s*\\)/;\nconst importNameRE = new RegExp(\n `\\\\b(?<importName>${namePattern}(\\\\.${namePattern})*)${importRE.source}`,\n);\nconst dependencyRE = /(?<pkgName>[^\"]+)?:(?<depID>[^\"]+)/; // use it to parse <moduleName> from importPattern or <templateName> from getTemplateID\n\n/**\n * Parse compiler option string representation\n * Compiler option line is a comment starting with '//tengo:', say\n * //tengo:hash_override tralala\n *\n * The common compiler option syntax is:\n * //tengo:<option name> [<option arg1> [<option arg 2> [...]]]\n */\nconst parseComplierOption = (opt: string): CompilerOption => {\n const parts = opt.split(\" \");\n const namePart = parts[0].split(\":\");\n if (namePart.length != 2) {\n throw new Error(\n \"compiler option format is wrong: expect to have option name after 'tengo:' prefix, like 'tengo:MyOption'\",\n );\n }\n const optName = namePart[1];\n\n return {\n name: optName,\n args: parts.slice(1),\n };\n};\n\nexport class ArtifactSource {\n constructor(\n /** The mode this artifact was built (dev or dist) */\n public readonly compileMode: CompileMode,\n /** Full artifact id, including package version */\n public readonly fullName: FullArtifactName,\n /** Hash of the source code */\n public readonly sourceHash: string,\n /** Normalized source code */\n public readonly src: string,\n /** Path to source file where artifact came from */\n public readonly srcName: string,\n /** List of dependencies */\n public readonly dependencies: TypedArtifactName[],\n /** Additional compiler options detected in source code */\n public readonly compilerOptions: CompilerOption[],\n ) {}\n}\n\nexport function parseSourceFile(\n logger: MiLogger,\n mode: CompileMode,\n srcFile: string,\n fullSourceName: FullArtifactName,\n normalize: boolean,\n): ArtifactSource {\n const src = readFileSync(srcFile).toString();\n const { deps, normalized, opts } = parseSourceData(logger, src, fullSourceName, normalize);\n\n return new ArtifactSource(\n mode,\n fullSourceName,\n getSha256(normalized),\n normalized,\n srcFile,\n deps.array,\n opts,\n );\n}\n\nexport function parseSource(\n logger: MiLogger,\n mode: CompileMode,\n src: string,\n fullSourceName: FullArtifactName,\n normalize: boolean,\n): ArtifactSource {\n const { deps, normalized, opts } = parseSourceData(logger, src, fullSourceName, normalize);\n\n return new ArtifactSource(\n mode,\n fullSourceName,\n getSha256(normalized),\n normalized,\n \"\",\n deps.array,\n opts,\n );\n}\n\n/**\n * Reads src\n * returns normalized source code,\n * gets dependencies from imports,\n * maps imports to global names if globalizeImports is true,\n * and collects compiler options like hashOverride.\n */\nfunction parseSourceData(\n logger: MiLogger,\n src: string,\n fullSourceName: FullArtifactName,\n globalizeImports: boolean,\n): {\n normalized: string;\n deps: ArtifactMap<TypedArtifactName>;\n opts: CompilerOption[];\n} {\n const dependencySet = createArtifactNameSet();\n const optionList: CompilerOption[] = [];\n\n // iterating over lines\n const lines = src.split(\"\\n\");\n\n // processedLines keep all the original lines from <src>.\n // If <globalizeImport>==true, the parser modifies 'import' and 'getTemplateId' lines\n // with Platforma Tengo lib and template usages, resolving local names (\":<item>\") to\n // global (\"@milaboratory/pkg:<item>\")\n const processedLines: string[] = [];\n let parserContext: sourceParserContext = {\n isInCommentBlock: false,\n canDetectOptions: true,\n artifactImportREs: new Map<string, [ArtifactType, RegExp][]>(),\n importLikeREs: new Map<string, [ArtifactType, RegExp][]>(),\n multilineStatement: \"\",\n lineNo: 0,\n };\n\n for (const line of lines) {\n parserContext.lineNo++;\n\n try {\n const {\n line: processedLine,\n context: newContext,\n artifacts,\n option,\n } = parseSingleSourceLine(logger, line, parserContext, fullSourceName.pkg, globalizeImports);\n processedLines.push(processedLine);\n parserContext = newContext;\n\n for (const artifact of artifacts ?? []) {\n dependencySet.add(artifact);\n }\n if (option) {\n optionList.push(option);\n }\n } catch (error: unknown) {\n const err = error as Error;\n throw new Error(\n `[line ${parserContext.lineNo} in ${fullNameToString(fullSourceName)}]: ${err.message}\\n\\t${line}`,\n { cause: err },\n );\n }\n }\n\n return {\n normalized: processedLines.join(\"\\n\"),\n deps: dependencySet,\n opts: optionList,\n };\n}\n\nexport type sourceParserContext = {\n isInCommentBlock: boolean;\n canDetectOptions: boolean;\n artifactImportREs: Map<string, [ArtifactType, RegExp][]>;\n importLikeREs: Map<string, [ArtifactType, RegExp][]>;\n multilineStatement: string;\n lineNo: number;\n};\n\nexport type lineProcessingResult = {\n line: string;\n context: sourceParserContext;\n artifacts: TypedArtifactName[];\n option: CompilerOption | undefined;\n};\n\nexport function parseSingleSourceLine(\n logger: MiLogger,\n line: string,\n context: sourceParserContext,\n localPackageName: string,\n globalizeImports?: boolean,\n): lineProcessingResult {\n if (context.isInCommentBlock) {\n if (multilineCommentEndRE.exec(line)) {\n context.isInCommentBlock = false;\n }\n return { line: \"\", context, artifacts: [], option: undefined };\n }\n\n if (compilerOptionRE.exec(line)) {\n if (!context.canDetectOptions) {\n logger.error(\n `[line ${context.lineNo}]: compiler option '//tengo:' was detected, but it cannot be applied as compiler options can be set only at the file header, before any code line'`,\n );\n throw new Error(\n \"tengo compiler options ('//tengo:' comments) can be set only in file header\",\n );\n }\n return { line, context, artifacts: [], option: parseComplierOption(line) };\n }\n\n if (wrongCompilerOptionRE.exec(line) && context.canDetectOptions) {\n logger.warn(\n `[line ${context.lineNo}]: text simillar to compiler option ('//tengo:...') was detected, but it has wrong format. Leave it as is, if you did not mean to use a line as compiler option. Or format it to '//tengo:<option>' otherwise (no spaces between '//' and 'tengo', no spaces between ':' and option name)`,\n );\n return { line, context, artifacts: [], option: undefined };\n }\n\n if (singlelineCommentRE.test(line) || singlelineTerminatedCommentRE.test(line)) {\n return { line: \"\", context, artifacts: [], option: undefined };\n }\n\n const canBeInlinedComment = line.includes(\"*/\");\n if (multilineCommentStartRE.exec(line) && !canBeInlinedComment) {\n context.isInCommentBlock = true;\n return { line: \"\", context, artifacts: [], option: undefined };\n }\n\n const statement = context.multilineStatement + line.trim();\n\n const mayContainAComment = line.includes(\"//\") || line.includes(\"/*\");\n if (multilineStatementRE.test(line) && !mayContainAComment) {\n // We accumulate multiline statements into single line before analyzing them.\n // This dramatically simplifies parsing logic: things like\n //\n // assets.\n // importSoftware(\"a:b\");\n //\n // become simple 'assets.importSoftware(\"a:b\");' for parser checks.\n //\n // For safety reasons, we never consider anything that 'may look like a comment'\n // as a part of multiline statement to prevent joining things like\n //\n // someFnCall() // looks like multiline statement because of dot in the end of a comment.\n //\n // This problem also appears in multiline string literals, but I hope this will not\n // cause problems in real life.\n\n // We still try to process each line to globalize imports in case of complex constructions, when\n // statements are stacked one into another:\n // a.\n // use(assets.importSoftware(\":soft1\")).\n // use(assets.importSoftware(\":soft2\")).\n // run()\n // It is multiline, and it still requires import globalization mid-way, not just for the last line of statement\n const result = processAssetImport(line, statement, context, localPackageName, globalizeImports);\n context.multilineStatement += result.line.trim(); // accumulate the line after imports globalization.\n return result;\n }\n\n context.multilineStatement = \"\"; // reset accumulated multiline statement parts once we reach statement end.\n\n if (emptyLineRE.exec(statement)) {\n return { line, context, artifacts: [], option: undefined };\n }\n\n // options could be only at the top of the file.\n context.canDetectOptions = false;\n\n return processAssetImport(line, statement, context, localPackageName, globalizeImports);\n}\n\nfunction processModuleImport(\n importInstruction: RegExpExecArray,\n originalLine: string,\n statement: string,\n context: sourceParserContext,\n localPackageName: string,\n globalizeImports?: boolean,\n): lineProcessingResult {\n const iInfo = parseImport(statement);\n\n // If we have plapi, ll or assets, then try to parse\n // getTemplateId, getSoftwareInfo, getSoftware and getAsset calls.\n\n if (iInfo.module === \"plapi\") {\n if (!context.artifactImportREs.has(iInfo.module)) {\n context.artifactImportREs.set(iInfo.module, [\n [\"template\", newGetTemplateIdRE(iInfo.alias)],\n [\"software\", newGetSoftwareInfoRE(iInfo.alias)],\n ]);\n }\n return { line: originalLine, context, artifacts: [], option: undefined };\n }\n\n if (\n iInfo.module === \"@milaboratory/tengo-sdk:ll\" ||\n iInfo.module === \"@platforma-sdk/workflow-tengo:ll\" ||\n ((localPackageName === \"@milaboratory/tengo-sdk\" ||\n localPackageName === \"@platforma-sdk/workflow-tengo\") &&\n iInfo.module === \":ll\")\n ) {\n if (!context.artifactImportREs.has(iInfo.module)) {\n context.artifactImportREs.set(iInfo.module, [\n [\"template\", newImportTemplateRE(iInfo.alias)],\n [\"software\", newImportSoftwareRE(iInfo.alias)],\n ]);\n }\n }\n\n if (\n iInfo.module === \"@milaboratory/tengo-sdk:assets\" ||\n iInfo.module === \"@platforma-sdk/workflow-tengo:assets\" ||\n ((localPackageName === \"@milaboratory/tengo-sdk\" ||\n localPackageName === \"@platforma-sdk/workflow-tengo\") &&\n iInfo.module === \":assets\")\n ) {\n if (!context.artifactImportREs.has(iInfo.module)) {\n context.artifactImportREs.set(iInfo.module, [\n [\"template\", newImportTemplateRE(iInfo.alias)],\n [\"software\", newImportSoftwareRE(iInfo.alias)],\n [\"asset\", newImportAssetRE(iInfo.alias)],\n [\"wasm\", newImportWasmRE(iInfo.alias)],\n ]);\n context.importLikeREs.set(iInfo.module, [\n [\"template\", newImportTemplateDetector(iInfo.alias)],\n [\"software\", newImportSoftwareDetector(iInfo.alias)],\n [\"asset\", newImportAssetDetector(iInfo.alias)],\n [\"wasm\", newImportWasmDetector(iInfo.alias)],\n ]);\n }\n }\n\n const artifact = parseArtifactName(iInfo.module, \"library\", localPackageName);\n if (!artifact) {\n // not a Platforma Tengo library import\n return { line: originalLine, context, artifacts: [], option: undefined };\n }\n\n if (globalizeImports) {\n originalLine = originalLine.replace(\n importInstruction[0],\n ` := import(\"${artifact.pkg}:${artifact.id}\")`,\n );\n }\n\n return { line: originalLine, context, artifacts: [artifact], option: undefined };\n}\n\nfunction processAssetImport(\n originalLine: string,\n statement: string,\n context: sourceParserContext,\n localPackageName: string,\n globalizeImports?: boolean,\n): lineProcessingResult {\n if (emptyLineRE.exec(statement)) {\n return { line: originalLine, context, artifacts: [], option: undefined };\n }\n\n // options could be only at the top of the file.\n context.canDetectOptions = false;\n\n const importInstruction = importRE.exec(statement);\n\n if (importInstruction) {\n return processModuleImport(\n importInstruction,\n originalLine,\n statement,\n context,\n localPackageName,\n globalizeImports,\n );\n }\n\n if (context.artifactImportREs.size > 0) {\n for (const [_, artifactRE] of context.artifactImportREs) {\n for (const [artifactType, re] of artifactRE) {\n // Find all matches in the statement\n const matches = Array.from(statement.matchAll(re));\n if (matches.length === 0) {\n continue;\n }\n\n const artifacts: TypedArtifactName[] = [];\n for (let i = matches.length - 1; i >= 0; i--) {\n const match = matches[i];\n if (!match || !match.groups) {\n continue;\n }\n\n const { fnCall, templateName, fnName } = match.groups;\n\n if (!fnCall || !templateName || !fnName) {\n throw Error(`failed to parse template import statement`);\n }\n\n const artifact = parseArtifactName(templateName, artifactType, localPackageName);\n if (!artifact) {\n throw Error(`failed to parse artifact name in ${fnName} import statement`);\n }\n artifacts.push(artifact);\n\n if (globalizeImports) {\n // Replace all occurrences of this fnCall in originalLine.\n // fnCall ends right after the literal's closing quote + ws;\n // the trailing `)` or `,` is preserved from the original\n // line, so multi-arg calls (`fn(\"name\", opts)`) keep their\n // remaining args intact.\n originalLine = originalLine.replaceAll(\n fnCall,\n `${fnName}(\"${artifact.pkg}:${artifact.id}\"`,\n );\n }\n }\n\n return { line: originalLine, context, artifacts, option: undefined };\n }\n }\n }\n\n if (context.importLikeREs.size > 0) {\n for (const [_, artifactRE] of context.importLikeREs) {\n for (const [artifactType, re] of artifactRE) {\n const match = re.exec(statement);\n if (!match || !match.groups) {\n continue;\n }\n\n throw Error(\n `incorrect '${artifactType}' import statement: use string literal as ID (variables are not allowed) in the same line with brackets (i.e. 'importSoftware(\"sw:main\")').`,\n );\n }\n }\n }\n\n return { line: originalLine, context, artifacts: [], option: undefined };\n}\n\ninterface ImportInfo {\n module: string; // the module name without wrapping quotes: import(\"<module>\")\n alias: string; // the name of variable that keeps imported module: <alias> := import(\"<module>\")\n}\n\nfunction parseImport(line: string): ImportInfo {\n const match = importNameRE.exec(line);\n\n if (!match || !match.groups) {\n throw Error(`failed to parse 'import' statement`);\n }\n\n const { importName, moduleName } = match.groups;\n if (!importName || !moduleName) {\n throw Error(`failed to parse 'import' statement`);\n }\n\n return {\n module: moduleName, // the module name without wrapping quotes: import(\"<module>\")\n alias: importName, // the name of variable that keeps imported module: <alias> := import(\"<module>\")\n };\n}\n\nfunction parseArtifactName(\n moduleName: string,\n aType: ArtifactType,\n localPackageName: string,\n): TypedArtifactName | undefined {\n const depInfo = dependencyRE.exec(moduleName);\n if (!depInfo) {\n return;\n }\n\n if (!depInfo.groups) {\n throw Error(\n `failed to parse dependency name inside 'import' statement. The dependency name should have format '<package>:<templateName>'`,\n );\n }\n\n const { pkgName, depID } = depInfo.groups;\n if (!depID) {\n throw Error(\n `failed to parse dependency name inside 'import' statement. The dependency name should have format '<package>:<templateName>'`,\n );\n }\n\n return { type: aType, pkg: pkgName ?? localPackageName, id: depID };\n}\n\nexport function getSha256(source: string): string {\n return createHash(\"sha256\").update(source).digest(\"hex\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"source.js","names":[],"sources":["../../src/compiler/source.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport {\n type TypedArtifactName,\n type FullArtifactName,\n type ArtifactType,\n type CompileMode,\n type CompilerOption,\n fullNameToString,\n} from \"./package\";\nimport type { ArtifactMap } from \"./artifactset\";\nimport { createArtifactNameSet } from \"./artifactset\";\nimport { createHash } from \"node:crypto\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\n\n// matches any valid name in tengo. Don't forget to use '\\b' when needed to limit the boundaries!\nconst namePattern = \"[_a-zA-Z][_a-zA-Z0-9]*\";\n\nconst functionCallRE = (moduleName: string, fnName: string) => {\n // `fnCall` captures from the function name through the first string\n // argument (including the closing quote and any trailing whitespace);\n // the closing-paren-or-comma check is a non-consuming lookahead, so\n // any extra args (`assets.importWasm(\"name\", { memoryLimit: ... })`)\n // survive the rewrite in `compiler.ts` — only the literal is\n // build-time material, anything past it is runtime config.\n return new RegExp(\n `\\\\b${moduleName}\\\\.(?<fnCall>(?<fnName>` +\n fnName +\n `)\\\\s*\\\\(\\\\s*\"(?<templateName>[^\"]+)\"\\\\s*)(?=[,)])`,\n \"g\",\n );\n};\n\nconst functionCallLikeRE = (moduleName: string, fnName: string) => {\n return new RegExp(`\\\\b${moduleName}\\\\.(?<fnName>` + fnName + `)\\\\s*\\\\(`, \"g\");\n};\n\nexport const newGetTemplateIdRE = (moduleName: string) =>\n functionCallRE(moduleName, \"getTemplateId\");\nexport const newGetSoftwareInfoRE = (moduleName: string) =>\n functionCallRE(moduleName, \"getSoftwareInfo\");\n\nconst newImportTemplateRE = (moduleName: string) => functionCallRE(moduleName, \"importTemplate\");\nconst newImportTemplateDetector = (moduleName: string) =>\n functionCallLikeRE(moduleName, \"importTemplate\");\nconst newImportSoftwareRE = (moduleName: string) => functionCallRE(moduleName, \"importSoftware\");\nconst newImportSoftwareDetector = (moduleName: string) =>\n functionCallLikeRE(moduleName, \"importSoftware\");\nconst newImportAssetRE = (moduleName: string) => functionCallRE(moduleName, \"importAsset\");\nconst newImportAssetDetector = (moduleName: string) =>\n functionCallLikeRE(moduleName, \"importAsset\");\nconst newImportWasmRE = (moduleName: string) => functionCallRE(moduleName, \"importWasm\");\nconst newImportWasmDetector = (moduleName: string) => functionCallLikeRE(moduleName, \"importWasm\");\n\nconst emptyLineRE = /^\\s*$/;\nconst compilerOptionRE = /^\\/\\/tengo:[\\w]/;\nconst wrongCompilerOptionRE = /^\\s*\\/\\/\\s*tengo:\\s*./;\nconst singlelineCommentRE = /^\\s*(\\/\\/)/;\nconst singlelineTerminatedCommentRE = /^\\s*\\/\\*.*\\*\\/\\s*$/; // matches '^/* ... */$' comment lines as a special case of singleline comments.\nconst multilineCommentStartRE = /^\\s*\\/\\*/;\nconst multilineCommentEndRE = /\\*\\//;\nconst multilineStatementRE = /[.,]\\s*$/; // it is hard to consistently treat (\\n\"a\"\\n) multiline statements, we forbid them for now.\n\n// import could only be an assignment in a statement,\n// other ways could break a compilation.\nconst importRE = /\\s*:=\\s*import\\s*\\(\\s*\"(?<moduleName>[^\"]+)\"\\s*\\)/;\nconst importNameRE = new RegExp(\n `\\\\b(?<importName>${namePattern}(\\\\.${namePattern})*)${importRE.source}`,\n);\nconst dependencyRE = /(?<pkgName>[^\"]+)?:(?<depID>[^\"]+)/; // use it to parse <moduleName> from importPattern or <templateName> from getTemplateID\n\n/**\n * Parse compiler option string representation\n * Compiler option line is a comment starting with '//tengo:', say\n * //tengo:hash_override tralala\n *\n * The common compiler option syntax is:\n * //tengo:<option name> [<option arg1> [<option arg 2> [...]]]\n */\nconst parseComplierOption = (opt: string): CompilerOption => {\n const parts = opt.split(\" \");\n const namePart = parts[0].split(\":\");\n if (namePart.length != 2) {\n throw new Error(\n \"compiler option format is wrong: expect to have option name after 'tengo:' prefix, like 'tengo:MyOption'\",\n );\n }\n const optName = namePart[1];\n\n return {\n name: optName,\n args: parts.slice(1),\n };\n};\n\nexport class ArtifactSource {\n constructor(\n /** The mode this artifact was built (dev or dist) */\n public readonly compileMode: CompileMode,\n /** Full artifact id, including package version */\n public readonly fullName: FullArtifactName,\n /** Hash of the source code */\n public readonly sourceHash: string,\n /** Normalized source code */\n public readonly src: string,\n /** Path to source file where artifact came from */\n public readonly srcName: string,\n /** List of dependencies */\n public readonly dependencies: TypedArtifactName[],\n /** Additional compiler options detected in source code */\n public readonly compilerOptions: CompilerOption[],\n ) {}\n}\n\nexport function parseSourceFile(\n logger: MiLogger,\n mode: CompileMode,\n srcFile: string,\n fullSourceName: FullArtifactName,\n normalize: boolean,\n): ArtifactSource {\n const src = readFileSync(srcFile).toString();\n const { deps, normalized, opts } = parseSourceData(logger, src, fullSourceName, normalize);\n\n return new ArtifactSource(\n mode,\n fullSourceName,\n getSha256(normalized),\n normalized,\n srcFile,\n deps.array,\n opts,\n );\n}\n\nexport function parseSource(\n logger: MiLogger,\n mode: CompileMode,\n src: string,\n fullSourceName: FullArtifactName,\n normalize: boolean,\n): ArtifactSource {\n const { deps, normalized, opts } = parseSourceData(logger, src, fullSourceName, normalize);\n\n return new ArtifactSource(\n mode,\n fullSourceName,\n getSha256(normalized),\n normalized,\n \"\",\n deps.array,\n opts,\n );\n}\n\n/**\n * Reads src\n * returns normalized source code,\n * gets dependencies from imports,\n * maps imports to global names if globalizeImports is true,\n * and collects compiler options like hashOverride.\n */\nfunction parseSourceData(\n logger: MiLogger,\n src: string,\n fullSourceName: FullArtifactName,\n globalizeImports: boolean,\n): {\n normalized: string;\n deps: ArtifactMap<TypedArtifactName>;\n opts: CompilerOption[];\n} {\n const dependencySet = createArtifactNameSet();\n const optionList: CompilerOption[] = [];\n\n // iterating over lines\n const lines = src.split(\"\\n\");\n\n // processedLines keep all the original lines from <src>.\n // If <globalizeImport>==true, the parser modifies 'import' and 'getTemplateId' lines\n // with Platforma Tengo lib and template usages, resolving local names (\":<item>\") to\n // global (\"@milaboratory/pkg:<item>\")\n const processedLines: string[] = [];\n let parserContext: sourceParserContext = {\n isInCommentBlock: false,\n canDetectOptions: true,\n artifactImportREs: new Map<string, [ArtifactType, RegExp][]>(),\n importLikeREs: new Map<string, [ArtifactType, RegExp][]>(),\n multilineStatement: \"\",\n lineNo: 0,\n };\n\n for (const line of lines) {\n parserContext.lineNo++;\n\n try {\n const {\n line: processedLine,\n context: newContext,\n artifacts,\n option,\n } = parseSingleSourceLine(logger, line, parserContext, fullSourceName.pkg, globalizeImports);\n processedLines.push(processedLine);\n parserContext = newContext;\n\n for (const artifact of artifacts ?? []) {\n dependencySet.add(artifact);\n }\n if (option) {\n optionList.push(option);\n }\n } catch (error: unknown) {\n const err = error as Error;\n throw new Error(\n `[line ${parserContext.lineNo} in ${fullNameToString(fullSourceName)}]: ${err.message}\\n\\t${line}`,\n { cause: err },\n );\n }\n }\n\n return {\n normalized: processedLines.join(\"\\n\"),\n deps: dependencySet,\n opts: optionList,\n };\n}\n\nexport type sourceParserContext = {\n isInCommentBlock: boolean;\n canDetectOptions: boolean;\n artifactImportREs: Map<string, [ArtifactType, RegExp][]>;\n importLikeREs: Map<string, [ArtifactType, RegExp][]>;\n multilineStatement: string;\n lineNo: number;\n};\n\nexport type lineProcessingResult = {\n line: string;\n context: sourceParserContext;\n artifacts: TypedArtifactName[];\n option: CompilerOption | undefined;\n /** Set when the line is a module import, for every module: Platforma\n * artifacts, `plapi` and the Tengo standard library alike. */\n moduleImport?: ImportInfo;\n};\n\nexport function parseSingleSourceLine(\n logger: MiLogger,\n line: string,\n context: sourceParserContext,\n localPackageName: string,\n globalizeImports?: boolean,\n): lineProcessingResult {\n if (context.isInCommentBlock) {\n if (multilineCommentEndRE.exec(line)) {\n context.isInCommentBlock = false;\n }\n return { line: \"\", context, artifacts: [], option: undefined };\n }\n\n if (compilerOptionRE.exec(line)) {\n if (!context.canDetectOptions) {\n logger.error(\n `[line ${context.lineNo}]: compiler option '//tengo:' was detected, but it cannot be applied as compiler options can be set only at the file header, before any code line'`,\n );\n throw new Error(\n \"tengo compiler options ('//tengo:' comments) can be set only in file header\",\n );\n }\n return { line, context, artifacts: [], option: parseComplierOption(line) };\n }\n\n if (wrongCompilerOptionRE.exec(line) && context.canDetectOptions) {\n logger.warn(\n `[line ${context.lineNo}]: text simillar to compiler option ('//tengo:...') was detected, but it has wrong format. Leave it as is, if you did not mean to use a line as compiler option. Or format it to '//tengo:<option>' otherwise (no spaces between '//' and 'tengo', no spaces between ':' and option name)`,\n );\n return { line, context, artifacts: [], option: undefined };\n }\n\n if (singlelineCommentRE.test(line) || singlelineTerminatedCommentRE.test(line)) {\n return { line: \"\", context, artifacts: [], option: undefined };\n }\n\n const canBeInlinedComment = line.includes(\"*/\");\n if (multilineCommentStartRE.exec(line) && !canBeInlinedComment) {\n context.isInCommentBlock = true;\n return { line: \"\", context, artifacts: [], option: undefined };\n }\n\n const statement = context.multilineStatement + line.trim();\n\n const mayContainAComment = line.includes(\"//\") || line.includes(\"/*\");\n if (multilineStatementRE.test(line) && !mayContainAComment) {\n // We accumulate multiline statements into single line before analyzing them.\n // This dramatically simplifies parsing logic: things like\n //\n // assets.\n // importSoftware(\"a:b\");\n //\n // become simple 'assets.importSoftware(\"a:b\");' for parser checks.\n //\n // For safety reasons, we never consider anything that 'may look like a comment'\n // as a part of multiline statement to prevent joining things like\n //\n // someFnCall() // looks like multiline statement because of dot in the end of a comment.\n //\n // This problem also appears in multiline string literals, but I hope this will not\n // cause problems in real life.\n\n // We still try to process each line to globalize imports in case of complex constructions, when\n // statements are stacked one into another:\n // a.\n // use(assets.importSoftware(\":soft1\")).\n // use(assets.importSoftware(\":soft2\")).\n // run()\n // It is multiline, and it still requires import globalization mid-way, not just for the last line of statement\n const result = processAssetImport(line, statement, context, localPackageName, globalizeImports);\n context.multilineStatement += result.line.trim(); // accumulate the line after imports globalization.\n return result;\n }\n\n context.multilineStatement = \"\"; // reset accumulated multiline statement parts once we reach statement end.\n\n if (emptyLineRE.exec(statement)) {\n return { line, context, artifacts: [], option: undefined };\n }\n\n // options could be only at the top of the file.\n context.canDetectOptions = false;\n\n return processAssetImport(line, statement, context, localPackageName, globalizeImports);\n}\n\nfunction processModuleImport(\n importInstruction: RegExpExecArray,\n originalLine: string,\n statement: string,\n context: sourceParserContext,\n localPackageName: string,\n globalizeImports?: boolean,\n): lineProcessingResult {\n const iInfo = parseImport(statement);\n\n // If we have plapi, ll or assets, then try to parse\n // getTemplateId, getSoftwareInfo, getSoftware and getAsset calls.\n\n if (iInfo.module === \"plapi\") {\n if (!context.artifactImportREs.has(iInfo.module)) {\n context.artifactImportREs.set(iInfo.module, [\n [\"template\", newGetTemplateIdRE(iInfo.alias)],\n [\"software\", newGetSoftwareInfoRE(iInfo.alias)],\n ]);\n }\n return { line: originalLine, context, artifacts: [], option: undefined, moduleImport: iInfo };\n }\n\n if (\n iInfo.module === \"@milaboratory/tengo-sdk:ll\" ||\n iInfo.module === \"@platforma-sdk/workflow-tengo:ll\" ||\n ((localPackageName === \"@milaboratory/tengo-sdk\" ||\n localPackageName === \"@platforma-sdk/workflow-tengo\") &&\n iInfo.module === \":ll\")\n ) {\n if (!context.artifactImportREs.has(iInfo.module)) {\n context.artifactImportREs.set(iInfo.module, [\n [\"template\", newImportTemplateRE(iInfo.alias)],\n [\"software\", newImportSoftwareRE(iInfo.alias)],\n ]);\n }\n }\n\n if (\n iInfo.module === \"@milaboratory/tengo-sdk:assets\" ||\n iInfo.module === \"@platforma-sdk/workflow-tengo:assets\" ||\n ((localPackageName === \"@milaboratory/tengo-sdk\" ||\n localPackageName === \"@platforma-sdk/workflow-tengo\") &&\n iInfo.module === \":assets\")\n ) {\n if (!context.artifactImportREs.has(iInfo.module)) {\n context.artifactImportREs.set(iInfo.module, [\n [\"template\", newImportTemplateRE(iInfo.alias)],\n [\"software\", newImportSoftwareRE(iInfo.alias)],\n [\"asset\", newImportAssetRE(iInfo.alias)],\n [\"wasm\", newImportWasmRE(iInfo.alias)],\n ]);\n context.importLikeREs.set(iInfo.module, [\n [\"template\", newImportTemplateDetector(iInfo.alias)],\n [\"software\", newImportSoftwareDetector(iInfo.alias)],\n [\"asset\", newImportAssetDetector(iInfo.alias)],\n [\"wasm\", newImportWasmDetector(iInfo.alias)],\n ]);\n }\n }\n\n const artifact = parseArtifactName(iInfo.module, \"library\", localPackageName);\n if (!artifact) {\n // not a Platforma Tengo library import\n return { line: originalLine, context, artifacts: [], option: undefined, moduleImport: iInfo };\n }\n\n if (globalizeImports) {\n originalLine = originalLine.replace(\n importInstruction[0],\n ` := import(\"${artifact.pkg}:${artifact.id}\")`,\n );\n }\n\n return {\n line: originalLine,\n context,\n artifacts: [artifact],\n option: undefined,\n moduleImport: iInfo,\n };\n}\n\nfunction processAssetImport(\n originalLine: string,\n statement: string,\n context: sourceParserContext,\n localPackageName: string,\n globalizeImports?: boolean,\n): lineProcessingResult {\n if (emptyLineRE.exec(statement)) {\n return { line: originalLine, context, artifacts: [], option: undefined };\n }\n\n // options could be only at the top of the file.\n context.canDetectOptions = false;\n\n const importInstruction = importRE.exec(statement);\n\n if (importInstruction) {\n return processModuleImport(\n importInstruction,\n originalLine,\n statement,\n context,\n localPackageName,\n globalizeImports,\n );\n }\n\n if (context.artifactImportREs.size > 0) {\n for (const [_, artifactRE] of context.artifactImportREs) {\n for (const [artifactType, re] of artifactRE) {\n // Find all matches in the statement\n const matches = Array.from(statement.matchAll(re));\n if (matches.length === 0) {\n continue;\n }\n\n const artifacts: TypedArtifactName[] = [];\n for (let i = matches.length - 1; i >= 0; i--) {\n const match = matches[i];\n if (!match || !match.groups) {\n continue;\n }\n\n const { fnCall, templateName, fnName } = match.groups;\n\n if (!fnCall || !templateName || !fnName) {\n throw Error(`failed to parse template import statement`);\n }\n\n const artifact = parseArtifactName(templateName, artifactType, localPackageName);\n if (!artifact) {\n throw Error(`failed to parse artifact name in ${fnName} import statement`);\n }\n artifacts.push(artifact);\n\n if (globalizeImports) {\n // Replace all occurrences of this fnCall in originalLine.\n // fnCall ends right after the literal's closing quote + ws;\n // the trailing `)` or `,` is preserved from the original\n // line, so multi-arg calls (`fn(\"name\", opts)`) keep their\n // remaining args intact.\n originalLine = originalLine.replaceAll(\n fnCall,\n `${fnName}(\"${artifact.pkg}:${artifact.id}\"`,\n );\n }\n }\n\n return { line: originalLine, context, artifacts, option: undefined };\n }\n }\n }\n\n if (context.importLikeREs.size > 0) {\n for (const [_, artifactRE] of context.importLikeREs) {\n for (const [artifactType, re] of artifactRE) {\n const match = re.exec(statement);\n if (!match || !match.groups) {\n continue;\n }\n\n throw Error(\n `incorrect '${artifactType}' import statement: use string literal as ID (variables are not allowed) in the same line with brackets (i.e. 'importSoftware(\"sw:main\")').`,\n );\n }\n }\n }\n\n return { line: originalLine, context, artifacts: [], option: undefined };\n}\n\nexport interface ImportInfo {\n module: string; // the module name without wrapping quotes: import(\"<module>\")\n alias: string; // the name of variable that keeps imported module: <alias> := import(\"<module>\")\n}\n\nfunction parseImport(line: string): ImportInfo {\n const match = importNameRE.exec(line);\n\n if (!match || !match.groups) {\n throw Error(`failed to parse 'import' statement`);\n }\n\n const { importName, moduleName } = match.groups;\n if (!importName || !moduleName) {\n throw Error(`failed to parse 'import' statement`);\n }\n\n return {\n module: moduleName, // the module name without wrapping quotes: import(\"<module>\")\n alias: importName, // the name of variable that keeps imported module: <alias> := import(\"<module>\")\n };\n}\n\nfunction parseArtifactName(\n moduleName: string,\n aType: ArtifactType,\n localPackageName: string,\n): TypedArtifactName | undefined {\n const depInfo = dependencyRE.exec(moduleName);\n if (!depInfo) {\n return;\n }\n\n if (!depInfo.groups) {\n throw Error(\n `failed to parse dependency name inside 'import' statement. The dependency name should have format '<package>:<templateName>'`,\n );\n }\n\n const { pkgName, depID } = depInfo.groups;\n if (!depID) {\n throw Error(\n `failed to parse dependency name inside 'import' statement. The dependency name should have format '<package>:<templateName>'`,\n );\n }\n\n return { type: aType, pkg: pkgName ?? localPackageName, id: depID };\n}\n\nexport function getSha256(source: string): string {\n return createHash(\"sha256\").update(source).digest(\"hex\");\n}\n"],"mappings":"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"}
|
package/dist/compiler/util.cjs
CHANGED
|
@@ -44,9 +44,28 @@ function pathType(path) {
|
|
|
44
44
|
function isUUID(uuid) {
|
|
45
45
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(uuid.toLowerCase());
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Collect all '.tengo' files in the directory and its subdirectories.
|
|
49
|
+
*
|
|
50
|
+
* The depth limit keeps the walk cheap: without it a scan started in a wrong
|
|
51
|
+
* place can become unbearably slow and even get the node process OOM killed.
|
|
52
|
+
* Increase the limit if you need to.
|
|
53
|
+
*/
|
|
54
|
+
function getTengoFiles(dir, depth) {
|
|
55
|
+
if (depth === 0) return [];
|
|
56
|
+
const files = node_fs.readdirSync(dir, { withFileTypes: true });
|
|
57
|
+
let tengoFiles = [];
|
|
58
|
+
files.forEach((file) => {
|
|
59
|
+
const absPath = node_path.join(dir, file.name);
|
|
60
|
+
if (file.isDirectory()) tengoFiles = tengoFiles.concat(getTengoFiles(absPath, depth - 1));
|
|
61
|
+
if (!file.isDirectory() && file.name.endsWith(".tengo")) tengoFiles.push(absPath);
|
|
62
|
+
});
|
|
63
|
+
return tengoFiles;
|
|
64
|
+
}
|
|
47
65
|
//#endregion
|
|
48
66
|
exports.assertNever = assertNever;
|
|
49
67
|
exports.createLogger = createLogger;
|
|
68
|
+
exports.getTengoFiles = getTengoFiles;
|
|
50
69
|
exports.isUUID = isUUID;
|
|
51
70
|
exports.pathType = pathType;
|
|
52
71
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.cjs","names":["fs"],"sources":["../../src/compiler/util.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as winston from \"winston\";\n\nexport function assertNever(x: never): never {\n // eslint-disable-next-line @typescript-eslint/restrict-plus-operands\n throw new Error(\"Unexpected object: \" + x);\n}\n\nexport function createLogger(level: string = \"debug\"): winston.Logger {\n return winston.createLogger({\n level: level,\n format: winston.format.combine(\n winston.format.errors({ stack: true }),\n winston.format.printf(({ level, message, stack }) => {\n const baseMessage = `${level.padStart(6, \" \")}: ${message as string}`;\n return stack ? `${baseMessage}\\n${stack as string}` : baseMessage;\n }),\n ),\n transports: [\n new winston.transports.Console({\n stderrLevels: [\"error\", \"warn\", \"info\", \"debug\"],\n handleExceptions: true,\n }),\n ],\n });\n}\n\nexport function findNodeModules(): string {\n let currentDir = process.cwd();\n\n while (currentDir) {\n const possibleNodeModulesPath = path.join(currentDir, \"node_modules\");\n\n if (fs.existsSync(possibleNodeModulesPath)) return possibleNodeModulesPath;\n\n const parentDir = path.resolve(currentDir, \"..\");\n if (parentDir === currentDir) break; // reached the root directory\n\n currentDir = parentDir;\n }\n\n throw new Error(\"Unable to find node_modules directory.\");\n}\n\nexport type PathType = \"absent\" | \"file\" | \"dir\" | \"link\" | \"unknown\";\n\nexport function pathType(path: string): PathType {\n try {\n const s = fs.statSync(path);\n if (s.isDirectory()) return \"dir\";\n if (s.isFile()) return \"file\";\n if (s.isSymbolicLink()) return \"link\";\n return \"unknown\";\n } catch (error: unknown) {\n const err = error as NodeJS.ErrnoException;\n if (err.code == \"ENOENT\") return \"absent\";\n if (err.code == \"ENOTDIR\") return \"absent\";\n else throw err;\n }\n}\n\nexport function isUUID(uuid: string): boolean {\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n return uuidRegex.test(uuid.toLowerCase());\n}\n"],"mappings":";;;;;;;;AAIA,SAAgB,YAAY,GAAiB;CAE3C,MAAM,IAAI,MAAM,wBAAwB,CAAC;AAC3C;AAEA,SAAgB,aAAa,QAAgB,SAAyB;CACpE,OAAO,QAAQ,aAAa;EACnB;EACP,QAAQ,QAAQ,OAAO,QACrB,QAAQ,OAAO,OAAO,EAAE,OAAO,KAAK,CAAC,GACrC,QAAQ,OAAO,QAAQ,EAAE,OAAO,SAAS,YAAY;GACnD,MAAM,cAAc,GAAG,MAAM,SAAS,GAAG,GAAG,EAAE,IAAI;GAClD,OAAO,QAAQ,GAAG,YAAY,IAAI,UAAoB;EACxD,CAAC,CACH;EACA,YAAY,CACV,IAAI,QAAQ,WAAW,QAAQ;GAC7B,cAAc;IAAC;IAAS;IAAQ;IAAQ;GAAO;GAC/C,kBAAkB;EACpB,CAAC,CACH;CACF,CAAC;AACH;AAqBA,SAAgB,SAAS,MAAwB;CAC/C,IAAI;EACF,MAAM,IAAIA,QAAG,SAAS,IAAI;EAC1B,IAAI,EAAE,YAAY,GAAG,OAAO;EAC5B,IAAI,EAAE,OAAO,GAAG,OAAO;EACvB,IAAI,EAAE,eAAe,GAAG,OAAO;EAC/B,OAAO;CACT,SAAS,OAAgB;EACvB,MAAM,MAAM;EACZ,IAAI,IAAI,QAAQ,UAAU,OAAO;EACjC,IAAI,IAAI,QAAQ,WAAW,OAAO;OAC7B,MAAM;CACb;AACF;AAEA,SAAgB,OAAO,MAAuB;CAE5C,OAAO,6EAAU,KAAK,KAAK,YAAY,CAAC;AAC1C"}
|
|
1
|
+
{"version":3,"file":"util.cjs","names":["fs","path"],"sources":["../../src/compiler/util.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as winston from \"winston\";\n\nexport function assertNever(x: never): never {\n // eslint-disable-next-line @typescript-eslint/restrict-plus-operands\n throw new Error(\"Unexpected object: \" + x);\n}\n\nexport function createLogger(level: string = \"debug\"): winston.Logger {\n return winston.createLogger({\n level: level,\n format: winston.format.combine(\n winston.format.errors({ stack: true }),\n winston.format.printf(({ level, message, stack }) => {\n const baseMessage = `${level.padStart(6, \" \")}: ${message as string}`;\n return stack ? `${baseMessage}\\n${stack as string}` : baseMessage;\n }),\n ),\n transports: [\n new winston.transports.Console({\n stderrLevels: [\"error\", \"warn\", \"info\", \"debug\"],\n handleExceptions: true,\n }),\n ],\n });\n}\n\nexport function findNodeModules(): string {\n let currentDir = process.cwd();\n\n while (currentDir) {\n const possibleNodeModulesPath = path.join(currentDir, \"node_modules\");\n\n if (fs.existsSync(possibleNodeModulesPath)) return possibleNodeModulesPath;\n\n const parentDir = path.resolve(currentDir, \"..\");\n if (parentDir === currentDir) break; // reached the root directory\n\n currentDir = parentDir;\n }\n\n throw new Error(\"Unable to find node_modules directory.\");\n}\n\nexport type PathType = \"absent\" | \"file\" | \"dir\" | \"link\" | \"unknown\";\n\nexport function pathType(path: string): PathType {\n try {\n const s = fs.statSync(path);\n if (s.isDirectory()) return \"dir\";\n if (s.isFile()) return \"file\";\n if (s.isSymbolicLink()) return \"link\";\n return \"unknown\";\n } catch (error: unknown) {\n const err = error as NodeJS.ErrnoException;\n if (err.code == \"ENOENT\") return \"absent\";\n if (err.code == \"ENOTDIR\") return \"absent\";\n else throw err;\n }\n}\n\nexport function isUUID(uuid: string): boolean {\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n return uuidRegex.test(uuid.toLowerCase());\n}\n\n/**\n * Collect all '.tengo' files in the directory and its subdirectories.\n *\n * The depth limit keeps the walk cheap: without it a scan started in a wrong\n * place can become unbearably slow and even get the node process OOM killed.\n * Increase the limit if you need to.\n */\nexport function getTengoFiles(dir: string, depth: number): string[] {\n if (depth === 0) {\n return [];\n }\n\n const files = fs.readdirSync(dir, { withFileTypes: true });\n\n let tengoFiles: string[] = [];\n files.forEach((file) => {\n const absPath = path.join(dir, file.name);\n\n if (file.isDirectory()) {\n tengoFiles = tengoFiles.concat(getTengoFiles(absPath, depth - 1));\n }\n\n if (!file.isDirectory() && file.name.endsWith(\".tengo\")) {\n tengoFiles.push(absPath);\n }\n });\n\n return tengoFiles;\n}\n"],"mappings":";;;;;;;;AAIA,SAAgB,YAAY,GAAiB;CAE3C,MAAM,IAAI,MAAM,wBAAwB,CAAC;AAC3C;AAEA,SAAgB,aAAa,QAAgB,SAAyB;CACpE,OAAO,QAAQ,aAAa;EACnB;EACP,QAAQ,QAAQ,OAAO,QACrB,QAAQ,OAAO,OAAO,EAAE,OAAO,KAAK,CAAC,GACrC,QAAQ,OAAO,QAAQ,EAAE,OAAO,SAAS,YAAY;GACnD,MAAM,cAAc,GAAG,MAAM,SAAS,GAAG,GAAG,EAAE,IAAI;GAClD,OAAO,QAAQ,GAAG,YAAY,IAAI,UAAoB;EACxD,CAAC,CACH;EACA,YAAY,CACV,IAAI,QAAQ,WAAW,QAAQ;GAC7B,cAAc;IAAC;IAAS;IAAQ;IAAQ;GAAO;GAC/C,kBAAkB;EACpB,CAAC,CACH;CACF,CAAC;AACH;AAqBA,SAAgB,SAAS,MAAwB;CAC/C,IAAI;EACF,MAAM,IAAIA,QAAG,SAAS,IAAI;EAC1B,IAAI,EAAE,YAAY,GAAG,OAAO;EAC5B,IAAI,EAAE,OAAO,GAAG,OAAO;EACvB,IAAI,EAAE,eAAe,GAAG,OAAO;EAC/B,OAAO;CACT,SAAS,OAAgB;EACvB,MAAM,MAAM;EACZ,IAAI,IAAI,QAAQ,UAAU,OAAO;EACjC,IAAI,IAAI,QAAQ,WAAW,OAAO;OAC7B,MAAM;CACb;AACF;AAEA,SAAgB,OAAO,MAAuB;CAE5C,OAAO,6EAAU,KAAK,KAAK,YAAY,CAAC;AAC1C;;;;;;;;AASA,SAAgB,cAAc,KAAa,OAAyB;CAClE,IAAI,UAAU,GACZ,OAAO,CAAC;CAGV,MAAM,QAAQA,QAAG,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC;CAEzD,IAAI,aAAuB,CAAC;CAC5B,MAAM,SAAS,SAAS;EACtB,MAAM,UAAUC,UAAK,KAAK,KAAK,KAAK,IAAI;EAExC,IAAI,KAAK,YAAY,GACnB,aAAa,WAAW,OAAO,cAAc,SAAS,QAAQ,CAAC,CAAC;EAGlE,IAAI,CAAC,KAAK,YAAY,KAAK,KAAK,KAAK,SAAS,QAAQ,GACpD,WAAW,KAAK,OAAO;CAE3B,CAAC;CAED,OAAO;AACT"}
|
package/dist/compiler/util.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "node:path";
|
|
1
|
+
import * as path from "node:path";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as winston from "winston";
|
|
4
4
|
//#region src/compiler/util.ts
|
|
@@ -40,7 +40,25 @@ function pathType(path) {
|
|
|
40
40
|
function isUUID(uuid) {
|
|
41
41
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(uuid.toLowerCase());
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Collect all '.tengo' files in the directory and its subdirectories.
|
|
45
|
+
*
|
|
46
|
+
* The depth limit keeps the walk cheap: without it a scan started in a wrong
|
|
47
|
+
* place can become unbearably slow and even get the node process OOM killed.
|
|
48
|
+
* Increase the limit if you need to.
|
|
49
|
+
*/
|
|
50
|
+
function getTengoFiles(dir, depth) {
|
|
51
|
+
if (depth === 0) return [];
|
|
52
|
+
const files = fs.readdirSync(dir, { withFileTypes: true });
|
|
53
|
+
let tengoFiles = [];
|
|
54
|
+
files.forEach((file) => {
|
|
55
|
+
const absPath = path.join(dir, file.name);
|
|
56
|
+
if (file.isDirectory()) tengoFiles = tengoFiles.concat(getTengoFiles(absPath, depth - 1));
|
|
57
|
+
if (!file.isDirectory() && file.name.endsWith(".tengo")) tengoFiles.push(absPath);
|
|
58
|
+
});
|
|
59
|
+
return tengoFiles;
|
|
60
|
+
}
|
|
43
61
|
//#endregion
|
|
44
|
-
export { assertNever, createLogger, isUUID, pathType };
|
|
62
|
+
export { assertNever, createLogger, getTengoFiles, isUUID, pathType };
|
|
45
63
|
|
|
46
64
|
//# sourceMappingURL=util.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","names":[],"sources":["../../src/compiler/util.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as winston from \"winston\";\n\nexport function assertNever(x: never): never {\n // eslint-disable-next-line @typescript-eslint/restrict-plus-operands\n throw new Error(\"Unexpected object: \" + x);\n}\n\nexport function createLogger(level: string = \"debug\"): winston.Logger {\n return winston.createLogger({\n level: level,\n format: winston.format.combine(\n winston.format.errors({ stack: true }),\n winston.format.printf(({ level, message, stack }) => {\n const baseMessage = `${level.padStart(6, \" \")}: ${message as string}`;\n return stack ? `${baseMessage}\\n${stack as string}` : baseMessage;\n }),\n ),\n transports: [\n new winston.transports.Console({\n stderrLevels: [\"error\", \"warn\", \"info\", \"debug\"],\n handleExceptions: true,\n }),\n ],\n });\n}\n\nexport function findNodeModules(): string {\n let currentDir = process.cwd();\n\n while (currentDir) {\n const possibleNodeModulesPath = path.join(currentDir, \"node_modules\");\n\n if (fs.existsSync(possibleNodeModulesPath)) return possibleNodeModulesPath;\n\n const parentDir = path.resolve(currentDir, \"..\");\n if (parentDir === currentDir) break; // reached the root directory\n\n currentDir = parentDir;\n }\n\n throw new Error(\"Unable to find node_modules directory.\");\n}\n\nexport type PathType = \"absent\" | \"file\" | \"dir\" | \"link\" | \"unknown\";\n\nexport function pathType(path: string): PathType {\n try {\n const s = fs.statSync(path);\n if (s.isDirectory()) return \"dir\";\n if (s.isFile()) return \"file\";\n if (s.isSymbolicLink()) return \"link\";\n return \"unknown\";\n } catch (error: unknown) {\n const err = error as NodeJS.ErrnoException;\n if (err.code == \"ENOENT\") return \"absent\";\n if (err.code == \"ENOTDIR\") return \"absent\";\n else throw err;\n }\n}\n\nexport function isUUID(uuid: string): boolean {\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n return uuidRegex.test(uuid.toLowerCase());\n}\n"],"mappings":";;;;AAIA,SAAgB,YAAY,GAAiB;CAE3C,MAAM,IAAI,MAAM,wBAAwB,CAAC;AAC3C;AAEA,SAAgB,aAAa,QAAgB,SAAyB;CACpE,OAAO,QAAQ,aAAa;EACnB;EACP,QAAQ,QAAQ,OAAO,QACrB,QAAQ,OAAO,OAAO,EAAE,OAAO,KAAK,CAAC,GACrC,QAAQ,OAAO,QAAQ,EAAE,OAAO,SAAS,YAAY;GACnD,MAAM,cAAc,GAAG,MAAM,SAAS,GAAG,GAAG,EAAE,IAAI;GAClD,OAAO,QAAQ,GAAG,YAAY,IAAI,UAAoB;EACxD,CAAC,CACH;EACA,YAAY,CACV,IAAI,QAAQ,WAAW,QAAQ;GAC7B,cAAc;IAAC;IAAS;IAAQ;IAAQ;GAAO;GAC/C,kBAAkB;EACpB,CAAC,CACH;CACF,CAAC;AACH;AAqBA,SAAgB,SAAS,MAAwB;CAC/C,IAAI;EACF,MAAM,IAAI,GAAG,SAAS,IAAI;EAC1B,IAAI,EAAE,YAAY,GAAG,OAAO;EAC5B,IAAI,EAAE,OAAO,GAAG,OAAO;EACvB,IAAI,EAAE,eAAe,GAAG,OAAO;EAC/B,OAAO;CACT,SAAS,OAAgB;EACvB,MAAM,MAAM;EACZ,IAAI,IAAI,QAAQ,UAAU,OAAO;EACjC,IAAI,IAAI,QAAQ,WAAW,OAAO;OAC7B,MAAM;CACb;AACF;AAEA,SAAgB,OAAO,MAAuB;CAE5C,OAAO,6EAAU,KAAK,KAAK,YAAY,CAAC;AAC1C"}
|
|
1
|
+
{"version":3,"file":"util.js","names":[],"sources":["../../src/compiler/util.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as winston from \"winston\";\n\nexport function assertNever(x: never): never {\n // eslint-disable-next-line @typescript-eslint/restrict-plus-operands\n throw new Error(\"Unexpected object: \" + x);\n}\n\nexport function createLogger(level: string = \"debug\"): winston.Logger {\n return winston.createLogger({\n level: level,\n format: winston.format.combine(\n winston.format.errors({ stack: true }),\n winston.format.printf(({ level, message, stack }) => {\n const baseMessage = `${level.padStart(6, \" \")}: ${message as string}`;\n return stack ? `${baseMessage}\\n${stack as string}` : baseMessage;\n }),\n ),\n transports: [\n new winston.transports.Console({\n stderrLevels: [\"error\", \"warn\", \"info\", \"debug\"],\n handleExceptions: true,\n }),\n ],\n });\n}\n\nexport function findNodeModules(): string {\n let currentDir = process.cwd();\n\n while (currentDir) {\n const possibleNodeModulesPath = path.join(currentDir, \"node_modules\");\n\n if (fs.existsSync(possibleNodeModulesPath)) return possibleNodeModulesPath;\n\n const parentDir = path.resolve(currentDir, \"..\");\n if (parentDir === currentDir) break; // reached the root directory\n\n currentDir = parentDir;\n }\n\n throw new Error(\"Unable to find node_modules directory.\");\n}\n\nexport type PathType = \"absent\" | \"file\" | \"dir\" | \"link\" | \"unknown\";\n\nexport function pathType(path: string): PathType {\n try {\n const s = fs.statSync(path);\n if (s.isDirectory()) return \"dir\";\n if (s.isFile()) return \"file\";\n if (s.isSymbolicLink()) return \"link\";\n return \"unknown\";\n } catch (error: unknown) {\n const err = error as NodeJS.ErrnoException;\n if (err.code == \"ENOENT\") return \"absent\";\n if (err.code == \"ENOTDIR\") return \"absent\";\n else throw err;\n }\n}\n\nexport function isUUID(uuid: string): boolean {\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n return uuidRegex.test(uuid.toLowerCase());\n}\n\n/**\n * Collect all '.tengo' files in the directory and its subdirectories.\n *\n * The depth limit keeps the walk cheap: without it a scan started in a wrong\n * place can become unbearably slow and even get the node process OOM killed.\n * Increase the limit if you need to.\n */\nexport function getTengoFiles(dir: string, depth: number): string[] {\n if (depth === 0) {\n return [];\n }\n\n const files = fs.readdirSync(dir, { withFileTypes: true });\n\n let tengoFiles: string[] = [];\n files.forEach((file) => {\n const absPath = path.join(dir, file.name);\n\n if (file.isDirectory()) {\n tengoFiles = tengoFiles.concat(getTengoFiles(absPath, depth - 1));\n }\n\n if (!file.isDirectory() && file.name.endsWith(\".tengo\")) {\n tengoFiles.push(absPath);\n }\n });\n\n return tengoFiles;\n}\n"],"mappings":";;;;AAIA,SAAgB,YAAY,GAAiB;CAE3C,MAAM,IAAI,MAAM,wBAAwB,CAAC;AAC3C;AAEA,SAAgB,aAAa,QAAgB,SAAyB;CACpE,OAAO,QAAQ,aAAa;EACnB;EACP,QAAQ,QAAQ,OAAO,QACrB,QAAQ,OAAO,OAAO,EAAE,OAAO,KAAK,CAAC,GACrC,QAAQ,OAAO,QAAQ,EAAE,OAAO,SAAS,YAAY;GACnD,MAAM,cAAc,GAAG,MAAM,SAAS,GAAG,GAAG,EAAE,IAAI;GAClD,OAAO,QAAQ,GAAG,YAAY,IAAI,UAAoB;EACxD,CAAC,CACH;EACA,YAAY,CACV,IAAI,QAAQ,WAAW,QAAQ;GAC7B,cAAc;IAAC;IAAS;IAAQ;IAAQ;GAAO;GAC/C,kBAAkB;EACpB,CAAC,CACH;CACF,CAAC;AACH;AAqBA,SAAgB,SAAS,MAAwB;CAC/C,IAAI;EACF,MAAM,IAAI,GAAG,SAAS,IAAI;EAC1B,IAAI,EAAE,YAAY,GAAG,OAAO;EAC5B,IAAI,EAAE,OAAO,GAAG,OAAO;EACvB,IAAI,EAAE,eAAe,GAAG,OAAO;EAC/B,OAAO;CACT,SAAS,OAAgB;EACvB,MAAM,MAAM;EACZ,IAAI,IAAI,QAAQ,UAAU,OAAO;EACjC,IAAI,IAAI,QAAQ,WAAW,OAAO;OAC7B,MAAM;CACb;AACF;AAEA,SAAgB,OAAO,MAAuB;CAE5C,OAAO,6EAAU,KAAK,KAAK,YAAY,CAAC;AAC1C;;;;;;;;AASA,SAAgB,cAAc,KAAa,OAAyB;CAClE,IAAI,UAAU,GACZ,OAAO,CAAC;CAGV,MAAM,QAAQ,GAAG,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC;CAEzD,IAAI,aAAuB,CAAC;CAC5B,MAAM,SAAS,SAAS;EACtB,MAAM,UAAU,KAAK,KAAK,KAAK,KAAK,IAAI;EAExC,IAAI,KAAK,YAAY,GACnB,aAAa,WAAW,OAAO,cAAc,SAAS,QAAQ,CAAC,CAAC;EAGlE,IAAI,CAAC,KAAK,YAAY,KAAK,KAAK,KAAK,SAAS,QAAQ,GACpD,WAAW,KAAK,OAAO;CAE3B,CAAC;CAED,OAAO;AACT"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platforma-sdk/tengo-builder",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Pl Tengo Template Builder",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"bin": {
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"typescript": "7.0.2",
|
|
27
27
|
"vitest": "^4.1.3",
|
|
28
28
|
"@milaboratories/build-configs": "2.0.1",
|
|
29
|
-
"@milaboratories/ts-
|
|
30
|
-
"@milaboratories/ts-
|
|
29
|
+
"@milaboratories/ts-configs": "1.4.0",
|
|
30
|
+
"@milaboratories/ts-builder": "1.7.2"
|
|
31
31
|
},
|
|
32
32
|
"engines": {
|
|
33
33
|
"node": ">=22"
|
package/src/cli.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import buildCommand from "./commands/build";
|
|
3
3
|
import checkCommand from "./commands/check";
|
|
4
|
+
import importsCommand from "./commands/imports";
|
|
4
5
|
import testCommand from "./commands/test";
|
|
5
6
|
import dumpArtifactsCommand from "./commands/dump/artifacts";
|
|
6
7
|
import dumpSoftwareCommand from "./commands/dump/software";
|
|
@@ -11,6 +12,7 @@ export function buildProgram(): Command {
|
|
|
11
12
|
|
|
12
13
|
program.addCommand(buildCommand());
|
|
13
14
|
program.addCommand(checkCommand());
|
|
15
|
+
program.addCommand(importsCommand());
|
|
14
16
|
program.addCommand(testCommand());
|
|
15
17
|
|
|
16
18
|
// `dump` is a pure topic (no action): `pl-tengo dump` prints help.
|
package/src/commands/build.ts
CHANGED
|
@@ -2,9 +2,8 @@ import type { SpawnSyncReturns } from "node:child_process";
|
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
import { compile, savePacks, getPackageInfo } from "../compiler/main";
|
|
5
|
-
import { createLogger } from "../compiler/util";
|
|
5
|
+
import { createLogger, getTengoFiles } from "../compiler/util";
|
|
6
6
|
import * as opts from "../shared/basecmd";
|
|
7
|
-
import * as fs from "node:fs";
|
|
8
7
|
import * as fsp from "node:fs/promises";
|
|
9
8
|
import * as path from "node:path";
|
|
10
9
|
import type * as winston from "winston";
|
|
@@ -162,29 +161,6 @@ https://marketplace.visualstudio.com/items?itemName=jaydenlin.ctags-support`);
|
|
|
162
161
|
logger.info("Generation of tags is done.");
|
|
163
162
|
}
|
|
164
163
|
|
|
165
|
-
function getTengoFiles(dir: string, depth: number): string[] {
|
|
166
|
-
if (depth === 0) {
|
|
167
|
-
return [];
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const files = fs.readdirSync(dir, { withFileTypes: true });
|
|
171
|
-
|
|
172
|
-
let tengoFiles: string[] = [];
|
|
173
|
-
files.forEach((file) => {
|
|
174
|
-
const absPath = path.join(dir, file.name);
|
|
175
|
-
|
|
176
|
-
if (file.isDirectory()) {
|
|
177
|
-
tengoFiles = tengoFiles.concat(getTengoFiles(absPath, depth - 1));
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (!file.isDirectory() && file.name.endsWith(".tengo")) {
|
|
181
|
-
tengoFiles.push(absPath);
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
return tengoFiles;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
164
|
function toRelativePath(dir: string, files: string[]): string[] {
|
|
189
165
|
// Note that VS Code extension likes only relatives paths to the root of the opened dir.
|
|
190
166
|
return files.map((file) => file.replace(dir, "."));
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Command, Option } from "commander";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { createLogger, getTengoFiles, pathType } from "../compiler/util";
|
|
5
|
+
import { dropImportLines, findUnusedImports } from "../compiler/imports";
|
|
6
|
+
import { resolvePackageJsonRoot } from "../compiler/main";
|
|
7
|
+
import * as opts from "../shared/basecmd";
|
|
8
|
+
|
|
9
|
+
// The same limit the tags generation uses: deep enough for any real source
|
|
10
|
+
// tree, shallow enough to keep a scan started in a wrong place cheap.
|
|
11
|
+
const maxScanDepth = 9;
|
|
12
|
+
|
|
13
|
+
export default function importsCommand(): Command {
|
|
14
|
+
const cmd = new Command("imports").description("find imports tengo sources do not use");
|
|
15
|
+
|
|
16
|
+
cmd.argument("[paths...]", "source paths to scan (defaults to ./src)");
|
|
17
|
+
opts.addOptions(cmd, opts.GlobalOptions());
|
|
18
|
+
cmd.addOption(new Option("--check", "report unused imports and fail, change no file"));
|
|
19
|
+
cmd.addOption(new Option("--fix", "remove unused imports from the sources"));
|
|
20
|
+
|
|
21
|
+
cmd.action(async (paths: string[], o) => {
|
|
22
|
+
const logger = createLogger(o.logLevel as string);
|
|
23
|
+
|
|
24
|
+
if (o.check && o.fix) {
|
|
25
|
+
cmd.error("error: options '--check' and '--fix' cannot be used together");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Without an explicit mode: fix the sources when a human runs the command,
|
|
29
|
+
// report and fail when CI does. CI must never commit a change of its own.
|
|
30
|
+
const fix = o.fix ? true : o.check ? false : !process.env.CI;
|
|
31
|
+
|
|
32
|
+
const roots = paths.length == 0 ? ["./src"] : paths;
|
|
33
|
+
const localPackageName = getLocalPackageName();
|
|
34
|
+
|
|
35
|
+
let hasUnusedImports = false;
|
|
36
|
+
let hasBrokenSources = false;
|
|
37
|
+
|
|
38
|
+
for (const file of collectSources(roots)) {
|
|
39
|
+
const src = fs.readFileSync(file).toString();
|
|
40
|
+
|
|
41
|
+
let unused;
|
|
42
|
+
try {
|
|
43
|
+
unused = findUnusedImports(logger, src, localPackageName, file);
|
|
44
|
+
} catch (err: unknown) {
|
|
45
|
+
// A source we cannot parse is a problem for 'pl-tengo check' to
|
|
46
|
+
// report in full. Here we keep the file as it is and move on, so a
|
|
47
|
+
// single broken source does not block the cleanup of all the others.
|
|
48
|
+
logger.error(err);
|
|
49
|
+
hasBrokenSources = true;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (unused.length == 0) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
hasUnusedImports = true;
|
|
58
|
+
logger.warn(`${file}: unused imports: ${unused.map((im) => im.alias).join(", ")}`);
|
|
59
|
+
|
|
60
|
+
if (fix) {
|
|
61
|
+
logger.info(`fixing ${file}...`);
|
|
62
|
+
fs.writeFileSync(
|
|
63
|
+
file,
|
|
64
|
+
dropImportLines(
|
|
65
|
+
src,
|
|
66
|
+
unused.map((im) => im.lineNo),
|
|
67
|
+
),
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (hasBrokenSources || (hasUnusedImports && !fix)) {
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return cmd;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Package name of the sources we scan. Used to resolve local ':<item>'
|
|
81
|
+
* imports. An empty name keeps the parser working for a directory that is
|
|
82
|
+
* not a package. */
|
|
83
|
+
function getLocalPackageName(): string {
|
|
84
|
+
try {
|
|
85
|
+
const packageJson = fs.readFileSync(resolvePackageJsonRoot(process.cwd())).toString();
|
|
86
|
+
return (JSON.parse(packageJson) as { name?: string }).name ?? "";
|
|
87
|
+
} catch {
|
|
88
|
+
return "";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function collectSources(roots: string[]): string[] {
|
|
93
|
+
const sources: string[] = [];
|
|
94
|
+
|
|
95
|
+
for (const root of roots) {
|
|
96
|
+
const type = pathType(root);
|
|
97
|
+
if (type === "dir") {
|
|
98
|
+
sources.push(...getTengoFiles(path.resolve(root), maxScanDepth));
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (type === "file") {
|
|
102
|
+
sources.push(path.resolve(root));
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
throw new Error(`source path not found: ${root}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return sources;
|
|
110
|
+
}
|