From 1df69620ebb742dde8fed55693dd73154b1488e7 Mon Sep 17 00:00:00 2001 From: ylakhdar Date: Thu, 16 Jan 2025 12:29:15 -0500 Subject: [PATCH 1/4] add custom transformer https://coveord.atlassian.net/browse/KIT-3865 --- packages/atomic/project.json | 2 +- packages/atomic/scripts/build.mjs | 86 +++++++++++++++++++++++ packages/atomic/scripts/svg-transform.mjs | 66 +++++++++++++++++ packages/atomic/scripts/watch.mjs | 2 +- 4 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 packages/atomic/scripts/build.mjs create mode 100644 packages/atomic/scripts/svg-transform.mjs diff --git a/packages/atomic/project.json b/packages/atomic/project.json index a0f7b8a898b..b3d2d24cc16 100644 --- a/packages/atomic/project.json +++ b/packages/atomic/project.json @@ -34,7 +34,7 @@ "commands": [ "node --max_old_space_size=6144 ../../node_modules/@stencil/core/bin/stencil build", "node ./scripts/stencil-proxy.mjs", - "tsc -p tsconfig.lit.json", + "node ./scripts/build.mjs --config=tsconfig.lit.json", "esbuild src/autoloader/index.ts --format=esm --outfile=dist/atomic/autoloader/index.esm.js", "esbuild src/autoloader/index.ts --format=cjs --outfile=dist/atomic/autoloader/index.cjs.js" ], diff --git a/packages/atomic/scripts/build.mjs b/packages/atomic/scripts/build.mjs new file mode 100644 index 00000000000..e4829bf2bb4 --- /dev/null +++ b/packages/atomic/scripts/build.mjs @@ -0,0 +1,86 @@ +import {dirname, basename} from 'path'; +import {argv} from 'process'; +import { + readConfigFile, + sys, + parseJsonConfigFileContent, + getPreEmitDiagnostics, + createProgram, + flattenDiagnosticMessageText, +} from 'typescript'; +import svgTransformer from './svg-transform.mjs'; + +const args = argv.slice(2); +const configArg = args.find((arg) => arg.startsWith('--config=')); +if (configArg === undefined) { + throw new Error('Missing --config=[PATH] argument'); +} +const tsConfigPath = configArg.split('=')[1]; + +function loadTsConfig(configPath) { + const configFile = readConfigFile(configPath, sys.readFile); + if (configFile.error) { + throw new Error( + `Error loading tsconfig file: ${configFile.error.messageText}` + ); + } + return parseJsonConfigFileContent( + configFile.config, + sys, + dirname(configPath) + ); +} + +function emit(program) { + const targetSourceFile = undefined; + const cancellationToken = undefined; + const writeFile = undefined; + const emitOnlyDtsFiles = false; + const customTransformers = { + before: [svgTransformer], + }; + + return program.emit( + targetSourceFile, + cancellationToken, + writeFile, + emitOnlyDtsFiles, + customTransformers + ); +} + +function compileWithTransformer() { + console.log('Using tsconfig:', basename(tsConfigPath)); + const {options, fileNames} = loadTsConfig(tsConfigPath); + const program = createProgram(fileNames, options); + const emitResult = emit(program); + + const allDiagnostics = getPreEmitDiagnostics(program).concat( + emitResult.diagnostics + ); + + allDiagnostics.forEach((diagnostic) => { + if (diagnostic.file) { + const {line, character} = getLineAndCharacterOfPosition( + diagnostic.file, + diagnostic.start + ); + const message = flattenDiagnosticMessageText( + diagnostic.messageText, + '\n' + ); + + console.log( + `${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}` + ); + } else { + console.error(flattenDiagnosticMessageText(diagnostic.messageText, '\n')); + } + }); + + let exitCode = emitResult.emitSkipped ? 1 : 0; + console.log(`Process exiting with code '${exitCode}'.`); + process.exit(exitCode); +} + +compileWithTransformer(); diff --git a/packages/atomic/scripts/svg-transform.mjs b/packages/atomic/scripts/svg-transform.mjs new file mode 100644 index 00000000000..a30ef2e28b0 --- /dev/null +++ b/packages/atomic/scripts/svg-transform.mjs @@ -0,0 +1,66 @@ +import {readFileSync} from 'fs'; +import {basename, dirname, join, resolve} from 'path'; +import { + NodeFlags, + isImportDeclaration, + visitEachChild, + visitNode, +} from 'typescript'; + +function createStatement(factory, svgContent, variableName) { + const bindingName = undefined; + const exclamationToken = undefined; + const modifiers = []; + const { + createVariableStatement, + createVariableDeclarationList, + createVariableDeclaration, + createStringLiteral, + } = factory; + + if (variableName === undefined) { + throw new Error( + `Variable name is not defined for the import statement ${node.getText()}` + ); + } + + return createVariableStatement( + modifiers, + createVariableDeclarationList( + [ + createVariableDeclaration( + variableName, + bindingName, + exclamationToken, + createStringLiteral(svgContent) + ), + ], + NodeFlags.Const + ) + ); +} + +/** + * Custom SVG transformer to handle .svg imports. + */ +export default function svgTransformer(context) { + const {factory} = context; + + function visit(node) { + if (isImportDeclaration(node)) { + const importPath = node.moduleSpecifier.text; + if (importPath.endsWith('.svg')) { + console.log('Replacing SVG import:', basename(importPath)); + const dir = dirname(node.getSourceFile().fileName); + const svgPath = resolve(dir, importPath); + const svgContent = readFileSync(svgPath, 'utf8'); + const variableName = node.importClause?.name?.escapedText; + + return createStatement(factory, svgContent, variableName); + } + } + return visitEachChild(node, visit, context); + } + + return (sourceFile) => visitNode(sourceFile, visit); +} diff --git a/packages/atomic/scripts/watch.mjs b/packages/atomic/scripts/watch.mjs index e2613acc523..00499cfd5b8 100644 --- a/packages/atomic/scripts/watch.mjs +++ b/packages/atomic/scripts/watch.mjs @@ -5,7 +5,7 @@ function rebuild() { const commands = [ 'node --max_old_space_size=6144 ../../node_modules/@stencil/core/bin/stencil build', 'node ./scripts/stencil-proxy.mjs', - 'tsc -p tsconfig.lit.json', + 'node ./scripts/build.mjs --config=tsconfig.lit.json', 'esbuild src/autoloader/index.ts --format=esm --outfile=dist/atomic/autoloader/index.esm.js', 'esbuild src/autoloader/index.ts --format=cjs --outfile=dist/atomic/autoloader/index.cjs.js', ]; From cbca06b810f3cdc434f8db16c463f040bb69cfc2 Mon Sep 17 00:00:00 2001 From: GitHub Actions Bot <> Date: Thu, 16 Jan 2025 17:37:37 +0000 Subject: [PATCH 2/4] Add generated files --- .../atomic-angular/src/lib/stencil-generated/components.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts index 48ae804850d..293cd556a1c 100644 --- a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts +++ b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts @@ -3107,5 +3107,4 @@ export declare interface AtomicTimeframeFacet extends Components.AtomicTimeframe -import type {} from '@coveo/atomic/components'; import type {} from '@coveo/atomic/components'; \ No newline at end of file From 36777b0268f85f8168f439b3c8044327cd53fe40 Mon Sep 17 00:00:00 2001 From: ylakhdar Date: Thu, 16 Jan 2025 15:24:31 -0500 Subject: [PATCH 3/4] Update build.mjs --- packages/atomic/scripts/build.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/atomic/scripts/build.mjs b/packages/atomic/scripts/build.mjs index e4829bf2bb4..fe5f672349c 100644 --- a/packages/atomic/scripts/build.mjs +++ b/packages/atomic/scripts/build.mjs @@ -2,6 +2,7 @@ import {dirname, basename} from 'path'; import {argv} from 'process'; import { readConfigFile, + getLineAndCharacterOfPosition, sys, parseJsonConfigFileContent, getPreEmitDiagnostics, From 70a5c700fd5434a0e3407a703cb5dcdc0e02a7c4 Mon Sep 17 00:00:00 2001 From: ylakhdar Date: Mon, 20 Jan 2025 10:27:53 -0500 Subject: [PATCH 4/4] add jsdoc --- packages/atomic/scripts/build.mjs | 9 ++++++++ packages/atomic/scripts/svg-transform.mjs | 28 +++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/packages/atomic/scripts/build.mjs b/packages/atomic/scripts/build.mjs index fe5f672349c..3ea239f5a7c 100644 --- a/packages/atomic/scripts/build.mjs +++ b/packages/atomic/scripts/build.mjs @@ -50,6 +50,15 @@ function emit(program) { ); } +/** + * Compiles TypeScript files using a custom transformer. + * + * This function mimics the behavior of running `tsc -p tsconfig.json` but applies a custom SVG transformer + * to all TypeScript files. It loads the TypeScript configuration from the specified `tsconfig.json` file, + * creates a TypeScript program, and emits the compiled JavaScript files with the custom transformer applied. + * + * Info: https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API#a-minimal-compiler + */ function compileWithTransformer() { console.log('Using tsconfig:', basename(tsConfigPath)); const {options, fileNames} = loadTsConfig(tsConfigPath); diff --git a/packages/atomic/scripts/svg-transform.mjs b/packages/atomic/scripts/svg-transform.mjs index a30ef2e28b0..cc83d15e544 100644 --- a/packages/atomic/scripts/svg-transform.mjs +++ b/packages/atomic/scripts/svg-transform.mjs @@ -7,6 +7,34 @@ import { visitNode, } from 'typescript'; +/** + * Creates a TypeScript variable statement for an SVG import. + * + * This function generates a TypeScript variable statement that assigns the SVG content as a string literal + * to a variable. It is used as part of a custom TypeScript transformer to inline SVG content in the transpiled + * JavaScript files. + * + * @example + * The following TypeScript source file: + * ```ts + * // src/components/component.ts + * import Tick from '../../../images/checkbox.svg'; + * () => console.log(Tick); + * ``` + * + * Will be transpiled to (note that the SVG import statement has been replaced with the SVG content): + * ```js + * // dist/components/component.js + * const Tick = " ... "; + * () => console.log(Tick); + * ``` + * + * @param {NodeFactory} factory - The TypeScript factory object used to create AST nodes. + * @param {string} svgContent - The content of the SVG file as a string. + * @param {string} variableName - The name of the variable to which the SVG content will be assigned. + * @returns {VariableStatement} A TypeScript variable statement that assigns the SVG content to the variable. + * @throws If the variable name is not defined. + */ function createStatement(factory, svgContent, variableName) { const bindingName = undefined; const exclamationToken = undefined;