swc-project / swc-node

Faster ts-node without typecheck
MIT License
1.69k stars 69 forks source link

fix: fix import ts from node_modules #744

Closed yeliex closed 5 months ago

yeliex commented 7 months ago

as https://github.com/swc-project/swc-node/pull/727#pullrequestreview-1760882920 describes, when import .ts files from node_moduels, it throws error TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts".

it works well in 1.6.7

example scene: monorepo import .ts file in project, or server framework wants dynamic load routes in development

yeliex commented 7 months ago

@Brooooooklyn please take a look

weyert commented 6 months ago

Yes, would be nice if this change can be merged.

charleslai2000 commented 4 months ago

In order to load typescript file under node_modules, you also need change file register.js, remove the following codes:

    if (options.files && options.files.length) {
        if (PLATFORM === 'win32' &&
            options.files.every((file) => filename !== (0, path_1.resolve)(process.cwd(), file))) {
            return sourcecode;
        }
        if (PLATFORM !== 'win32' && options.files.every((file) => !filename.endsWith(file))) {
            return sourcecode;
        }
    }

These codes will limit only compile files in current project and all ts files under node_modules will be filtered out and emit to node. This is not suitable in this context.

yeliex commented 4 months ago

I think this code can be changed into:

const TS_EXTENSIONS = [ts.Extension.Ts, ts.Extension.Tsx, ts.Extension.Mts];
const JS_EXTENSIONS = ["js", "jsx", "cjs", "mjs"];

export const resolve = async (specifier, context, nextResolve) => {
    const isTS = TS_EXTENSIONS.some((ext) => specifier.endsWith(ext));
    const isJS = JS_EXTENSIONS.some((ext) => specifier.endsWith(ext));
    // entrypoint
    if (!context.parentURL) {
        return {
            format: isTS ? 'ts' : undefined,
            url: specifier,
            shortCircuit: true,
        };
    }
    // import/require javascript module
    if (isJS) {
      // - local project non-TS file
      return nextResolve(specifier);
    }
    const { resolvedModule } = ts.resolveModuleName(specifier, fileURLToPath(context.parentURL), tsconfig, host, moduleResolutionCache);
    // import from local project to local project TS file
    if (resolvedModule && TS_EXTENSIONS.includes(resolvedModule.extension)) {
        return {
            format: 'ts',
            url: pathToFileURL(resolvedModule.resolvedFileName).href,
            shortCircuit: true,
        };
    }
    // import from local project to either:
    // - something TS couldn't resolve
    // - external library
    return nextResolve(specifier);
};

This code will deal with soft links of monorepo subprojects under node_modules, in this cache, the specifier has no extension at all.

add another condition to detect js files is unnecessary. these would be coved by final case.

yeliex commented 4 months ago

In order to load typescript file under node_modules, you also need change file register.js, remove the following codes:

    if (options.files && options.files.length) {
        if (PLATFORM === 'win32' &&
            options.files.every((file) => filename !== (0, path_1.resolve)(process.cwd(), file))) {
            return sourcecode;
        }
        if (PLATFORM !== 'win32' && options.files.every((file) => !filename.endsWith(file))) {
            return sourcecode;
        }
    }

These codes will limit only compile files in current project and all ts files under node_modules will be filtered out and emit to node. This is not suitable in this context.

I think it works as expected. files should be added into options.files, or leaves the option empty. or please provide reproduction case it it has problem in your situation