import { path as pathModule, Plugin, type ProjectFile, Shape, StringShape, ts } from '@servicenow/sdk-build-core' import { applyPathMappings } from './utils' const HTML_IMPORT_PREFIX = `` const getDeclarationAndModuleSpecifierIfHtml = (node: ts.Node): [ts.ImportDeclaration, string] | null => { const [importClause] = node.getSymbol()?.getDeclarations() ?? [] if (!importClause) { return null } const importDeclaration = importClause.getParent()?.asKind(ts.SyntaxKind.ImportDeclaration) if (!importDeclaration) { return null } const moduleSpecifier = importDeclaration.getModuleSpecifierValue() if (!moduleSpecifier.endsWith('.html')) { return null } return [importDeclaration, moduleSpecifier] } export const HtmlImportPlugin = Plugin.create({ name: 'HtmlImportPlugin', noTelemetry: true, shapes: [ { shape: StringShape, commit(shape, target) { const results = getDeclarationAndModuleSpecifierIfHtml(target) if (!results) { return { success: false } } const shapeContent = shape.getValue().trim() // skip committing if the content has the import directive comment return { success: shapeContent.startsWith(HTML_IMPORT_PREFIX) } }, }, ], nodes: [ { node: 'Identifier', fileTypes: ['fluent'], toShape(node, context) { const { project, diagnostics, config } = context const results = getDeclarationAndModuleSpecifierIfHtml(node) if (!results) { return { success: false } } const [importDeclaration, moduleSpecifier] = results if (!/^\.\.?\/.*$/.test(moduleSpecifier.trim())) { diagnostics.error(importDeclaration, 'import must be a relative path') return { success: false } } const currentDir = pathModule.dirname(node.getSourceFile().getFilePath()) const resolvedPath = project.resolvePath(currentDir, moduleSpecifier) const projectRelativePath = pathModule.relative(project.getRootDir(), resolvedPath) if (projectRelativePath.startsWith('..')) { diagnostics.error(importDeclaration, `import not within project: ${moduleSpecifier}`) return { success: false } } const mappedPath = applyPathMappings(projectRelativePath, config.staticContent.paths) let htmlFile: ProjectFile try { htmlFile = project.addFile(mappedPath, { resolveDependencies: false, excludeFromCompiler: true }) } catch (e) { diagnostics.error( importDeclaration, `failed to add HTML file: ${e instanceof Error ? e.message : String(e)}` ) return { success: false } } return { success: true, // add a directive comment to the imported HTML content to warn users against modifing it value: Shape.from(node, `${HTML_IMPORT_PREFIX} ${HTML_IMPORT_WARNING}\n${htmlFile.getContent()}`), } }, }, ], })