Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When TS returns emitSkipped, fallback to transpileOnly #1629

Merged
merged 2 commits into from Feb 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .prettierignore
Expand Up @@ -11,3 +11,4 @@ tests/throw error.ts
tests/throw error react tsx.tsx
tests/esm/throw error.ts
tests/legacy-source-map-support-interop/index.ts
tests/main-realpath/symlink/symlink.tsx
30 changes: 17 additions & 13 deletions src/index.ts
Expand Up @@ -1016,7 +1016,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
if (diagnosticList.length) reportTSError(diagnosticList);

if (output.emitSkipped) {
throw new TypeError(`${relative(cwd, fileName)}: Emit skipped`);
return [undefined, undefined, true];
}

// Throw an error when requiring `.d.ts` files.
Expand All @@ -1029,7 +1029,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
);
}

return [output.outputFiles[1].text, output.outputFiles[0].text];
return [output.outputFiles[1].text, output.outputFiles[0].text, false];
};

getTypeInfo = (code: string, fileName: string, position: number) => {
Expand Down Expand Up @@ -1159,7 +1159,8 @@ export function create(rawOptions: CreateOptions = {}): Service {
};

getOutput = (code: string, fileName: string) => {
const output: [string, string] = ['', ''];
let outText = '';
let outMap = '';

updateMemoryCache(code, fileName);

Expand All @@ -1179,9 +1180,9 @@ export function create(rawOptions: CreateOptions = {}): Service {
sourceFile,
(path, file, writeByteOrderMark) => {
if (path.endsWith('.map')) {
output[1] = file;
outMap = file;
} else {
output[0] = file;
outText = file;
}

if (options.emit) sys.writeFile(path, file, writeByteOrderMark);
Expand All @@ -1192,11 +1193,11 @@ export function create(rawOptions: CreateOptions = {}): Service {
);

if (result.emitSkipped) {
throw new TypeError(`${relative(cwd, fileName)}: Emit skipped`);
return [undefined, undefined, true];
}

// Throw an error when requiring files that cannot be compiled.
if (output[0] === '') {
if (outText === '') {
if (program.isSourceFileFromExternalLibrary(sourceFile)) {
throw new TypeError(
`Unable to compile file from external library: ${relative(
Expand All @@ -1214,7 +1215,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
);
}

return output;
return [outText, outMap, false];
};

getTypeInfo = (code: string, fileName: string, position: number) => {
Expand Down Expand Up @@ -1291,7 +1292,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
);
if (diagnosticList.length) reportTSError(diagnosticList);

return [result.outputText, result.sourceMapText as string];
return [result.outputText, result.sourceMapText as string, false];
};
}

Expand All @@ -1308,24 +1309,27 @@ export function create(rawOptions: CreateOptions = {}): Service {
: createTranspileOnlyGetOutputFunction(
ts.ModuleKind.ES2020 || ts.ModuleKind.ES2015
);
const getOutputTranspileOnly = createTranspileOnlyGetOutputFunction();

// Create a simple TypeScript compiler proxy.
function compile(code: string, fileName: string, lineOffset = 0) {
const normalizedFileName = normalizeSlashes(fileName);
const classification =
moduleTypeClassifier.classifyModule(normalizedFileName);
// Must always call normal getOutput to throw typechecking errors
let [value, sourceMap] = getOutput(code, normalizedFileName);
let [value, sourceMap, emitSkipped] = getOutput(code, normalizedFileName);
// If module classification contradicts the above, call the relevant transpiler
if (classification.moduleType === 'cjs' && getOutputForceCommonJS) {
[value, sourceMap] = getOutputForceCommonJS(code, normalizedFileName);
} else if (classification.moduleType === 'esm' && getOutputForceESM) {
[value, sourceMap] = getOutputForceESM(code, normalizedFileName);
} else if (emitSkipped) {
[value, sourceMap] = getOutputTranspileOnly(code, normalizedFileName);
}
const output = updateOutput(
value,
value!,
normalizedFileName,
sourceMap,
sourceMap!,
getExtension
);
outputCache.set(normalizedFileName, { content: output });
Expand Down Expand Up @@ -1456,7 +1460,7 @@ function registerExtension(
/**
* Internal source output.
*/
type SourceOutput = [string, string];
type SourceOutput = [string, string, false] | [undefined, undefined, true];

/**
* Update the output remapping the source map.
Expand Down
11 changes: 11 additions & 0 deletions src/test/index.spec.ts
Expand Up @@ -1256,3 +1256,14 @@ test.suite('ts-node', (test) => {
}
});
});

test('Falls back to transpileOnly when ts compiler returns emitSkipped', async () => {
const { err, stdout } = await exec(
`${CMD_TS_NODE_WITHOUT_PROJECT_FLAG} --project tsconfig.json ./outside-rootDir/foo.js`,
{
cwd: join(TEST_DIR, 'emit-skipped-fallback'),
}
);
expect(err).toBe(null);
expect(stdout).toBe('foo\n');
});
15 changes: 15 additions & 0 deletions tests/emit-skipped-fallback/outside-rootDir/foo.js
@@ -0,0 +1,15 @@
// This file causes TS to return emitSkipped because it's outside of rootDir and
// it's .js. I assume this happens because the emit path is the same as the
// input path, and perhaps also because the file is classified "external"

const decorator = () => {};

class Foo {
// Using a decorator to prove this .js file is getting compiled
@decorator
method() {
return 'foo';
}
}

console.log(new Foo().method());
7 changes: 7 additions & 0 deletions tests/emit-skipped-fallback/tsconfig.json
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"allowJs": true,
"experimentalDecorators": true,
"rootDir": "rootDir"
}
}