1- // Adapted from,
2- // typescript-esm-example
3- // By Andrey Sakharov
4- // Original: https://github.com/muturgan/typescript-esm-example/blob/main/buildtools/fix-imports.js
5- // MIT License: https://github.com/muturgan/typescript-esm-example/blob/main/LICENSE
6-
7- import { extname , join } from 'path' ;
8- import { existsSync , readdirSync , readFileSync , statSync , writeFileSync } from 'fs' ;
9-
10- const START_PATH = join ( process . cwd ( ) , 'dist/esm' ) ;
11- const IMPORT_REGEXP = / ^ ( ( i m p o r t | e x p o r t ) [ ^ ' ; ] * f r o m " ( \. \/ | ( \. \. \/ ) + ) [ ^ " ; ] * ) " / g;
12- const JUST_ADD_AN_EXTENSION = `$1.js"` ;
13- const ADD_INDEX_FILE = `$1/index.js"` ;
14- const JS_EXT = '.js' ;
1+ import { existsSync , writeFileSync } from 'fs'
2+ import { dirname , join } from "path"
3+ import ts from 'typescript'
4+ const { createPrinter, isSourceFile, createCompilerHost, createProgram, isImportDeclaration, isExportDeclaration, isStringLiteral, ModuleKind, ModuleResolutionKind, ScriptTarget } = ts
155
166/**
17- * @param {string } rootPath
7+ *
8+ * @param {ts.SourceFile } sourceFile
9+ * @returns {ts.TransformerFactory<ts.Node> }
1810 */
19- function fixImportsAtFolder ( rootPath ) {
20- const entries = readdirSync ( rootPath ) ;
11+ const generateTransformer = ( sourceFile ) => {
12+ const pathWithoutFileName = dirname ( sourceFile . fileName ) ;
2113
22- entries . forEach ( ( entry ) => {
23- const entryPath = join ( rootPath , entry ) ;
24- if ( entry . endsWith ( JS_EXT ) ) {
25- fixImportsAtFile ( entryPath ) ;
26- }
27- else {
28- const extName = extname ( entry ) ;
29- if ( ! extName ) {
30- const stat = statSync ( entryPath ) ;
31- if ( stat . isDirectory ( ) ) {
32- fixImportsAtFolder ( entryPath ) ;
14+ return ( /** @type {ts.TransformationContext } */ context ) => ( /** @type {ts.Node } */ rootNode ) => {
15+
16+ function visit ( /** @type {ts.Node } */ node ) {
17+ node = ts . visitEachChild ( node , visit , context ) ;
18+
19+ // Import Declaration
20+ const parentNodeIsExportOrImport = node . parent &&
21+ ( isImportDeclaration ( node . parent ) || isExportDeclaration ( node . parent ) )
22+
23+ // Node is the relative path of an import or export
24+ if ( parentNodeIsExportOrImport && isStringLiteral ( node ) ) {
25+ const relativePathWithQuotes = node . getFullText ( sourceFile ) . trimStart ( )
26+ const relativePathWithoutQuotes = relativePathWithQuotes . substring ( 1 , relativePathWithQuotes . length - 1 )
27+
28+ if ( relativePathWithoutQuotes . includes ( ".js" ) ) {
29+ return node
30+ }
31+
32+ const fullImportPath = join ( pathWithoutFileName , relativePathWithoutQuotes ) ;
33+
34+ // Append .js or index.js to all imports
35+ if ( existsSync ( fullImportPath + ".js" ) ) {
36+ node = context . factory . createStringLiteral ( `${ relativePathWithoutQuotes } .js` )
37+ }
38+ else if ( existsSync ( join ( fullImportPath , "index.js" ) ) ) {
39+ node = context . factory . createStringLiteral ( `${ relativePathWithoutQuotes } /index.js` )
40+ }
41+ else {
42+ throw new Error ( `Can't fix TypeScript paths: ${ node . getFullText ( sourceFile ) } ` )
3343 }
3444 }
45+
46+ return node
3547 }
36- } ) ;
48+
49+ return ts . visitNode ( rootNode , visit )
50+ }
3751}
3852
3953/**
40- *
41- * @param { string } filePath
54+ * Add .js extension to imports and exports
55+ * Specify index.js for folder imports
4256 */
43- function fixImportsAtFile ( filePath ) {
44- const content = readFileSync ( filePath ) . toString ( 'utf8' ) ;
45- const lines = content . split ( '\n' ) ;
46- const fixedLines = lines . map ( ( l ) => {
47- if ( ! l . match ( IMPORT_REGEXP ) ) {
48- return l ;
49- }
57+ const addJsToImportAndExports = ( ) => {
58+ /** @type {ts.CompilerOptions } */
59+ const compilerOptions = {
60+ moduleResolution : ModuleResolutionKind . NodeJs ,
61+ module : ModuleKind . NodeNext ,
62+ esModuleInterop : true ,
63+ target : ScriptTarget . ESNext ,
64+ lib : [
65+ "es2020"
66+ ] ,
67+ allowJs : true ,
68+ checkJs : true
69+ } ;
5070
51- const [ _ , importPath ] = l . split ( `"` ) ;
52- const fullPath = join ( filePath , '..' , importPath ) ;
53- const exists = existsSync ( fullPath ) ;
54- if ( exists === false ) {
55- return l . replace ( IMPORT_REGEXP , JUST_ADD_AN_EXTENSION ) ;
56- }
71+ const host = createCompilerHost ( compilerOptions ) ;
72+ const program = createProgram ( [ "./dist/esm/index.js" ] , compilerOptions , host ) ;
73+ const sourceFiles = program . getSourceFiles ( ) ;
5774
58- const stat = statSync ( fullPath ) ;
59- const isDirectory = stat . isDirectory ( ) ;
60- if ( isDirectory === true ) {
61- return l . replace ( IMPORT_REGEXP , ADD_INDEX_FILE ) ;
75+ for ( const sourceFile of sourceFiles ) {
76+ if ( ! sourceFile . isDeclarationFile ) {
77+ const result = ts . transform (
78+ sourceFile , [ generateTransformer ( sourceFile ) ]
79+ )
80+
81+ const transformedSourceFile = result . transformed [ 0 ]
82+ if ( isSourceFile ( transformedSourceFile ) ) {
83+ const printer = createPrinter ( )
84+ const file = printer . printFile ( transformedSourceFile )
85+ writeFileSync ( sourceFile . fileName , file )
86+ }
6287 }
63-
64- return l ;
65- } ) ;
66- const withFixedImports = fixedLines . join ( '\n' ) ;
67- writeFileSync ( filePath , withFixedImports ) ;
88+ }
6889}
6990
70- fixImportsAtFolder ( START_PATH ) ;
91+ addJsToImportAndExports ( )
0 commit comments