From 67f25b548d489cb21eb6d44f70706c96b9b9a5a1 Mon Sep 17 00:00:00 2001 From: nokazn <41154684+nokazn@users.noreply.github.com> Date: Tue, 12 Apr 2022 08:29:16 +0900 Subject: [PATCH 1/5] fix: load tsconfig.json in official TypeScript way --- src/index.js | 51 +++++++-------------------- src/utils/load-tsconfig-options.js | 55 ++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 39 deletions(-) create mode 100644 src/utils/load-tsconfig-options.js diff --git a/src/index.js b/src/index.js index 593ea088..f3ec14f0 100644 --- a/src/index.js +++ b/src/index.js @@ -6,13 +6,13 @@ const webpack = require("webpack"); const MemoryFS = require("memory-fs"); const terser = require("terser"); const tsconfigPaths = require("tsconfig-paths"); -const { loadTsconfig } = require("tsconfig-paths/lib/tsconfig-loader"); const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin"); const shebangRegEx = require('./utils/shebang'); const nccCacheDir = require("./utils/ncc-cache-dir"); const LicenseWebpackPlugin = require('license-webpack-plugin').LicenseWebpackPlugin; const { version: nccVersion } = require('../package.json'); const { hasTypeModule } = require('./utils/has-type-module'); +const { loadTsconfigOptions } = require('./utils/load-tsconfig-options'); // support glob graceful-fs fs.gracefulify(require("fs")); @@ -108,23 +108,18 @@ function ncc ( existingAssetNames.push(`${filename}.cache`); existingAssetNames.push(`${filename}.cache${ext}`); } + const compilerOptions = loadTsconfigOptions({ + base: process.cwd(), + start: dirname(entry), + filename: 'tsconfig.json' + }); const resolvePlugins = []; // add TsconfigPathsPlugin to support `paths` resolution in tsconfig // we need to catch here because the plugin will // error if there's no tsconfig in the working directory - let fullTsconfig = {}; try { - const configFileAbsolutePath = walkParentDirs({ - base: process.cwd(), - start: dirname(entry), - filename: 'tsconfig.json', - }); - fullTsconfig = loadTsconfig(configFileAbsolutePath) || { - compilerOptions: {} - }; - const tsconfigPathsOptions = { silent: true } - if (fullTsconfig.compilerOptions.allowJs) { + if (compilerOptions.allowJs) { tsconfigPathsOptions.extensions = SUPPORTED_EXTENSIONS } resolvePlugins.push(new TsconfigPathsPlugin(tsconfigPathsOptions)); @@ -361,7 +356,7 @@ function ncc ( compilerOptions: { module: 'esnext', target: 'esnext', - ...fullTsconfig.compilerOptions, + ...compilerOptions, allowSyntheticDefaultImports: true, noEmit: false, outDir: '//' @@ -452,7 +447,7 @@ function ncc ( async function finalizeHandler (stats) { const assets = Object.create(null); - getFlatFiles(mfs.data, assets, relocateLoader.getAssetMeta, fullTsconfig); + getFlatFiles(mfs.data, assets, relocateLoader.getAssetMeta, compilerOptions); // filter symlinks to existing assets const symlinks = Object.create(null); for (const [key, value] of Object.entries(relocateLoader.getSymlinks())) { @@ -646,17 +641,17 @@ function ncc ( } // this could be rewritten with actual FS apis / globs, but this is simpler -function getFlatFiles(mfsData, output, getAssetMeta, tsconfig, curBase = "") { +function getFlatFiles(mfsData, output, getAssetMeta, tsconfigCompilerOptions, curBase = "") { for (const path of Object.keys(mfsData)) { const item = mfsData[path]; let curPath = `${curBase}/${path}`; // directory - if (item[""] === true) getFlatFiles(item, output, getAssetMeta, tsconfig, curPath); + if (item[""] === true) getFlatFiles(item, output, getAssetMeta, tsconfigCompilerOptions, curPath); // file else if (!curPath.endsWith("/")) { const meta = getAssetMeta(curPath.slice(1)) || {}; if(curPath.endsWith(".d.ts")) { - const outDir = tsconfig.compilerOptions.outDir ? pathResolve(tsconfig.compilerOptions.outDir) : pathResolve('dist'); + const outDir = tsconfigCompilerOptions.outDir ? pathResolve(tsconfigCompilerOptions.outDir) : pathResolve('dist'); curPath = curPath .replace(outDir, "") .replace(process.cwd(), "") @@ -668,25 +663,3 @@ function getFlatFiles(mfsData, output, getAssetMeta, tsconfig, curBase = "") { } } } - -// Adapted from https://github.com/vercel/vercel/blob/18bec983aefbe2a77bd14eda6fca59ff7e956d8b/packages/build-utils/src/fs/run-user-scripts.ts#L289-L310 -function walkParentDirs({ - base, - start, - filename, -}) { - let parent = ''; - - for (let current = start; base.length <= current.length; current = parent) { - const fullPath = join(current, filename); - - // eslint-disable-next-line no-await-in-loop - if (fs.existsSync(fullPath)) { - return fullPath; - } - - parent = dirname(current); - } - - return null; -} diff --git a/src/utils/load-tsconfig-options.js b/src/utils/load-tsconfig-options.js new file mode 100644 index 00000000..78a85789 --- /dev/null +++ b/src/utils/load-tsconfig-options.js @@ -0,0 +1,55 @@ +const ts = require('typescript'); +const { join, dirname } = require('path'); +const fs = require('fs'); + +/** + * @typedef {object} LoadTsconfigInit + * @property {string} base + * @property {string} start + * @property {string} filename + */ + +/** + * @description Adapted from https://github.com/vercel/vercel/blob/18bec983aefbe2a77bd14eda6fca59ff7e956d8b/packages/build-utils/src/fs/run-user-scripts.ts#L289-L310 + * @param {LoadTsconfigInit} + * @returns {string | null} + */ +function walkParentDirs({ + base, + start, + filename, +}) { + let parent = ''; + + for (let current = start; base.length <= current.length; current = parent) { + const fullPath = join(current, filename); + + if (fs.existsSync(fullPath)) { + return fullPath; + } + + parent = dirname(current); + } + + return null; +} + +/** + * @param {LoadTsconfigInit} + * @returns {ts.CompilerOptions} + */ +exports.loadTsconfigOptions = function ({ + base, + start, + filename, +}) { + const tsconfig = walkParentDirs({ base, start, filename }); + if (tsconfig == null) { + return {}; + } + const content = ts.readConfigFile(tsconfig, ts.sys.readFile); + if (content.error != null || content.config == null) { + return {}; + } + return ts.parseJsonConfigFileContent(content.config, ts.sys, dirname(tsconfig)).options; +}; From 8d225934129ae68eeda571389d11a20195ed02ed Mon Sep 17 00:00:00 2001 From: nokazn <41154684+nokazn@users.noreply.github.com> Date: Tue, 12 Apr 2022 21:03:59 +0900 Subject: [PATCH 2/5] feat: add `--tsconfig-path` option as a CLI arg to specify path to `tsconfig.json` --- src/cli.js | 7 +++++-- src/index.js | 5 +++-- src/utils/load-tsconfig-options.js | 8 +++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/cli.js b/src/cli.js index eeeaebb7..3c335377 100755 --- a/src/cli.js +++ b/src/cli.js @@ -37,6 +37,7 @@ Options: --license [file] Adds a file containing licensing information to the output --stats-out [file] Emit webpack stats as json to the specified output file --target [es] ECMAScript target to use for output (default: es2015) + --tsconfig-path [file] Specify tsconfig.json to use for build (default: resolve tsconfig.json from entrypoint) Learn more: https://webpack.js.org/configuration/target -d, --debug Show debug logs `; @@ -154,7 +155,8 @@ async function runCmd (argv, stdout, stderr) { "-t": "--transpile-only", "--license": String, "--stats-out": String, - "--target": String + "--target": String, + "--tsconfig-path": String }, { permissive: false, argv @@ -250,7 +252,8 @@ async function runCmd (argv, stdout, stderr) { transpileOnly: args["--transpile-only"], license: args["--license"], quiet, - target: args["--target"] + target: args["--target"], + tsconfigPath: args["--tsconfig-path"] } ); diff --git a/src/index.js b/src/index.js index f3ec14f0..5d05a54d 100644 --- a/src/index.js +++ b/src/index.js @@ -57,7 +57,8 @@ function ncc ( production = true, // webpack defaults to `module` and `main`, but that's // not really what node.js supports, so we reset it - mainFields = ['main'] + mainFields = ['main'], + tsconfigPath = undefined } = {} ) { // v8 cache not supported for ES modules @@ -108,7 +109,7 @@ function ncc ( existingAssetNames.push(`${filename}.cache`); existingAssetNames.push(`${filename}.cache${ext}`); } - const compilerOptions = loadTsconfigOptions({ + const compilerOptions = loadTsconfigOptions(tsconfigPath, { base: process.cwd(), start: dirname(entry), filename: 'tsconfig.json' diff --git a/src/utils/load-tsconfig-options.js b/src/utils/load-tsconfig-options.js index 78a85789..30463c7d 100644 --- a/src/utils/load-tsconfig-options.js +++ b/src/utils/load-tsconfig-options.js @@ -1,5 +1,5 @@ const ts = require('typescript'); -const { join, dirname } = require('path'); +const { join, dirname, resolve } = require('path'); const fs = require('fs'); /** @@ -35,15 +35,17 @@ function walkParentDirs({ } /** + * @param {string | undefined} configPath * @param {LoadTsconfigInit} * @returns {ts.CompilerOptions} */ -exports.loadTsconfigOptions = function ({ +exports.loadTsconfigOptions = function (configPath, { base, start, filename, }) { - const tsconfig = walkParentDirs({ base, start, filename }); + // throw error if `configPath` does not exist + const tsconfig = configPath != null ? resolve(configPath) : walkParentDirs({ base, start, filename }); if (tsconfig == null) { return {}; } From 3302d618ef41fef5452f6a401911b31c8bcbfe12 Mon Sep 17 00:00:00 2001 From: nokazn <41154684+nokazn@users.noreply.github.com> Date: Wed, 13 Apr 2022 01:06:56 +0900 Subject: [PATCH 3/5] fix: convert & normalize enum key in compilerOptions at `tsconfig.json` --- package.json | 1 + readme.md | 1 + src/utils/load-tsconfig-options.js | 102 +++++++++++++++++++++++++---- yarn.lock | 36 ++++++++++ 4 files changed, 129 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 2efe3c0b..c2739d01 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "node-gyp": "^8.4.1", "npm": "^6.13.4", "oracledb": "^4.2.0", + "param-case": "^3.0.4", "passport": "^0.5.2", "passport-google-oauth": "^2.0.0", "path-platform": "^0.11.15", diff --git a/readme.md b/readme.md index f3485a8d..c42f958c 100644 --- a/readme.md +++ b/readme.md @@ -72,6 +72,7 @@ Outputs the Node.js compact build of `input.js` into `dist/index.js`. --license [file] Adds a file containing licensing information to the output --stats-out [file] Emit webpack stats as json to the specified output file --target [es] ECMAScript target to use for output (default: es2015) + --tsconfig-path [file] Specify tsconfig.json to use for build (default: resolve tsconfig.json from entrypoint) Learn more: https://webpack.js.org/configuration/target -d, --debug Show debug logs ``` diff --git a/src/utils/load-tsconfig-options.js b/src/utils/load-tsconfig-options.js index 30463c7d..3d1b43f9 100644 --- a/src/utils/load-tsconfig-options.js +++ b/src/utils/load-tsconfig-options.js @@ -1,6 +1,7 @@ const ts = require('typescript'); const { join, dirname, resolve } = require('path'); const fs = require('fs'); +const { paramCase } = require('param-case'); /** * @typedef {object} LoadTsconfigInit @@ -14,11 +15,7 @@ const fs = require('fs'); * @param {LoadTsconfigInit} * @returns {string | null} */ -function walkParentDirs({ - base, - start, - filename, -}) { +function walkParentDirs({ base, start, filename }) { let parent = ''; for (let current = start; base.length <= current.length; current = parent) { @@ -34,16 +31,98 @@ function walkParentDirs({ return null; } +/** + * @param {ts.CompilerOptions} options + * @param {string | undefined} key + * @param {(value: string) => string} [callback] + * @returns {string | undefined} + */ +function convertEnumCompilerOptions(enumCompilerOptions, key, callback) { + if (key == null) { + return undefined; + } + const value = enumCompilerOptions[key]; + return typeof callback === 'function' ? callback(value) : value; +} + +/** + * @param {string} value + * @returns {string} + */ +function toLowerCase(value) { + return value.toLowerCase(); +} + +/** + * @param {ts.NewLineKind} newLine + * @returns {string | undefined} + */ +function normalizeNewLineOption(newLine) { + switch (newLine) { + case ts.NewLineKind.CarriageReturnLineFeed: + return 'crlf'; + case ts.NewLineKind.LineFeed: + return 'lf'; + default: + return undefined; + } +} + +/** + * @param {ts.ModuleResolutionKind} moduleResolution + * @returns {string | undefined} + */ +function normalizeModuleResolutionOption(moduleResolution) { + switch (moduleResolution) { + case ts.ModuleResolutionKind.Classic: + return 'classic'; + case ts.ModuleResolutionKind.NodeJs: + return 'node'; + case ts.ModuleResolutionKind.Node12: + return 'node12'; + case ts.ModuleResolutionKind.NodeNext: + return 'nodenext'; + default: + return undefined; + } +} + +/** + * @param {ts.CompilerOptions} options + * @returns {ts.CompilerOptions} + */ +function normalizeCompilerOptions(options) { + if (options.importsNotUsedAsValues != null) { + options.importsNotUsedAsValues = convertEnumCompilerOptions( + ts.ImportsNotUsedAsValues, + options.importsNotUsedAsValues, + toLowerCase, + ); + } + if (options.jsx != null) { + options.jsx = convertEnumCompilerOptions(ts.JsxEmit, options.jsx, paramCase); + } + if (options.module != null) { + options.module = convertEnumCompilerOptions(ts.ModuleKind, options.module, toLowerCase); + } + if (options.moduleResolution != null) { + options.moduleResolution = normalizeModuleResolutionOption(options.moduleResolution); + } + if (options.newLine != null) { + options.newLine = normalizeNewLineOption(options.newLine); + } + if (options.target != null) { + options.target = convertEnumCompilerOptions(ts.ScriptTarget, options.target, toLowerCase); + } + return options; +} + /** * @param {string | undefined} configPath * @param {LoadTsconfigInit} * @returns {ts.CompilerOptions} */ -exports.loadTsconfigOptions = function (configPath, { - base, - start, - filename, -}) { +exports.loadTsconfigOptions = function (configPath, { base, start, filename }) { // throw error if `configPath` does not exist const tsconfig = configPath != null ? resolve(configPath) : walkParentDirs({ base, start, filename }); if (tsconfig == null) { @@ -53,5 +132,6 @@ exports.loadTsconfigOptions = function (configPath, { if (content.error != null || content.config == null) { return {}; } - return ts.parseJsonConfigFileContent(content.config, ts.sys, dirname(tsconfig)).options; + const { options } = ts.parseJsonConfigFileContent(content.config, ts.sys, dirname(tsconfig)); + return normalizeCompilerOptions(options); }; diff --git a/yarn.lock b/yarn.lock index 313bdd4f..03438934 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5512,6 +5512,14 @@ dot-case@^1.1.0: dependencies: sentence-case "^1.1.2" +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + dot-prop@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.1.tgz#45884194a71fc2cda71cbb4bceb3a4dd2f433ba4" @@ -9941,6 +9949,13 @@ lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2: resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + lowercase-keys@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" @@ -10747,6 +10762,14 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + node-abi@^2.21.0: version "2.30.0" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.30.0.tgz#8be53bf3e7945a34eea10e0fc9a5982776cf550b" @@ -11711,6 +11734,14 @@ param-case@^1.1.0: dependencies: sentence-case "^1.1.2" +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + parents@^1.0.0, parents@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751" @@ -14861,6 +14892,11 @@ tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.2.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== +tslib@^2.0.3: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + tslib@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" From bec1a972e1ac5024f1dddd22d39e22d6933cdcc3 Mon Sep 17 00:00:00 2001 From: nokazn <41154684+nokazn@users.noreply.github.com> Date: Wed, 13 Apr 2022 21:54:28 +0900 Subject: [PATCH 4/5] test: add test patterns for `--tsconfig-path` CLI option --- test/cli.js | 10 ++++++++++ test/fixtures/ts-extends/any-args.ts | 6 ++++++ test/fixtures/ts-extends/tsconfig.build.json | 6 ++++++ test/fixtures/ts-extends/tsconfig.json | 5 +++++ 4 files changed, 27 insertions(+) create mode 100644 test/fixtures/ts-extends/any-args.ts create mode 100644 test/fixtures/ts-extends/tsconfig.build.json create mode 100644 test/fixtures/ts-extends/tsconfig.json diff --git a/test/cli.js b/test/cli.js index 8df03fda..c29d6ff7 100644 --- a/test/cli.js +++ b/test/cli.js @@ -70,6 +70,16 @@ module.exports = [ args: ["run", "-t", "test/fixtures/with-type-errors/ts-error.ts"], expect: { code: 0 } }, + { + args: ["run", "test/fixtures/ts-extends/any-args.ts"], + expect (code, stdout, stderr) { + return code === 1 && stderr.toString().indexOf('any-args.ts(4,20)') !== -1 && stderr.toString().indexOf('TS7006') !== -1; + } + }, + { + args: ["run", "test/fixtures/ts-extends/any-args.ts", "--tsconfig-path", "test/fixtures/ts-extends/tsconfig.build.json"], + expect: { code: 0 } + }, { args: ["build", "-o", "tmp", "test/fixtures/test.cjs"], expect (code, stdout, stderr) { diff --git a/test/fixtures/ts-extends/any-args.ts b/test/fixtures/ts-extends/any-args.ts new file mode 100644 index 00000000..c9dcb73f --- /dev/null +++ b/test/fixtures/ts-extends/any-args.ts @@ -0,0 +1,6 @@ +/** + * throws error (TS7006: an implicit 'any' type) if strict options is set to true, or otherwise passes compilation + */ +function something(args) { + return args; +} diff --git a/test/fixtures/ts-extends/tsconfig.build.json b/test/fixtures/ts-extends/tsconfig.build.json new file mode 100644 index 00000000..5787af45 --- /dev/null +++ b/test/fixtures/ts-extends/tsconfig.build.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "strict": false + } +} diff --git a/test/fixtures/ts-extends/tsconfig.json b/test/fixtures/ts-extends/tsconfig.json new file mode 100644 index 00000000..aee0ec94 --- /dev/null +++ b/test/fixtures/ts-extends/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "strict": true + } +} From 5d85cd27bd3637cef366f7840c0515f6ee3d5f69 Mon Sep 17 00:00:00 2001 From: nokazn <41154684+nokazn@users.noreply.github.com> Date: Sat, 16 Apr 2022 12:11:28 +0900 Subject: [PATCH 5/5] refactor: modify way of parsing `tsconfig.json` --- package.json | 2 +- src/cli.js | 5 +- src/index.js | 16 ++--- src/utils/load-tsconfig-options.js | 110 ++++------------------------- yarn.lock | 41 ++--------- 5 files changed, 31 insertions(+), 143 deletions(-) diff --git a/package.json b/package.json index 4273c6f9..bf65cf00 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,6 @@ "node-gyp": "^8.4.1", "npm": "^6.13.4", "oracledb": "^4.2.0", - "param-case": "^3.0.4", "passport": "^0.5.2", "passport-google-oauth": "^2.0.0", "path-platform": "^0.11.15", @@ -101,6 +100,7 @@ "the-answer": "^1.0.0", "tiny-json-http": "^7.0.2", "ts-loader": "^8.3.0", + "tsconfck": "^1.2.2", "tsconfig-paths": "^3.7.0", "tsconfig-paths-webpack-plugin": "^3.2.0", "twilio": "^3.23.2", diff --git a/src/cli.js b/src/cli.js index 3c335377..9bbbf7de 100755 --- a/src/cli.js +++ b/src/cli.js @@ -346,8 +346,9 @@ async function runCmd (argv, stdout, stderr) { } } if (args["--watch"]) { - ncc.handler(handler); - ncc.rebuild(() => { + const nccWithWatchOption = await ncc + nccWithWatchOption.handler(handler); + nccWithWatchOption.rebuild(() => { if (ps) ps.kill(); startTime = Date.now(); diff --git a/src/index.js b/src/index.js index 5d05a54d..c3bf8af7 100644 --- a/src/index.js +++ b/src/index.js @@ -32,7 +32,7 @@ const defaultPermissions = 0o666; const relocateLoader = eval('require(__dirname + "/loaders/relocate-loader.js")'); module.exports = ncc; -function ncc ( +async function ncc ( entry, { cache, @@ -109,7 +109,7 @@ function ncc ( existingAssetNames.push(`${filename}.cache`); existingAssetNames.push(`${filename}.cache${ext}`); } - const compilerOptions = loadTsconfigOptions(tsconfigPath, { + const fullTsconfig = await loadTsconfigOptions(tsconfigPath, { base: process.cwd(), start: dirname(entry), filename: 'tsconfig.json' @@ -120,7 +120,7 @@ function ncc ( // error if there's no tsconfig in the working directory try { const tsconfigPathsOptions = { silent: true } - if (compilerOptions.allowJs) { + if (fullTsconfig.compilerOptions.allowJs) { tsconfigPathsOptions.extensions = SUPPORTED_EXTENSIONS } resolvePlugins.push(new TsconfigPathsPlugin(tsconfigPathsOptions)); @@ -357,7 +357,7 @@ function ncc ( compilerOptions: { module: 'esnext', target: 'esnext', - ...compilerOptions, + ...fullTsconfig.compilerOptions, allowSyntheticDefaultImports: true, noEmit: false, outDir: '//' @@ -448,7 +448,7 @@ function ncc ( async function finalizeHandler (stats) { const assets = Object.create(null); - getFlatFiles(mfs.data, assets, relocateLoader.getAssetMeta, compilerOptions); + getFlatFiles(mfs.data, assets, relocateLoader.getAssetMeta, fullTsconfig); // filter symlinks to existing assets const symlinks = Object.create(null); for (const [key, value] of Object.entries(relocateLoader.getSymlinks())) { @@ -642,17 +642,17 @@ function ncc ( } // this could be rewritten with actual FS apis / globs, but this is simpler -function getFlatFiles(mfsData, output, getAssetMeta, tsconfigCompilerOptions, curBase = "") { +function getFlatFiles(mfsData, output, getAssetMeta, tsconfig, curBase = "") { for (const path of Object.keys(mfsData)) { const item = mfsData[path]; let curPath = `${curBase}/${path}`; // directory - if (item[""] === true) getFlatFiles(item, output, getAssetMeta, tsconfigCompilerOptions, curPath); + if (item[""] === true) getFlatFiles(item, output, getAssetMeta, tsconfig, curPath); // file else if (!curPath.endsWith("/")) { const meta = getAssetMeta(curPath.slice(1)) || {}; if(curPath.endsWith(".d.ts")) { - const outDir = tsconfigCompilerOptions.outDir ? pathResolve(tsconfigCompilerOptions.outDir) : pathResolve('dist'); + const outDir = tsconfig.compilerOptions.outDir ? pathResolve(tsconfig.compilerOptions.outDir) : pathResolve('dist'); curPath = curPath .replace(outDir, "") .replace(process.cwd(), "") diff --git a/src/utils/load-tsconfig-options.js b/src/utils/load-tsconfig-options.js index 3d1b43f9..e7a997b2 100644 --- a/src/utils/load-tsconfig-options.js +++ b/src/utils/load-tsconfig-options.js @@ -1,7 +1,10 @@ -const ts = require('typescript'); const { join, dirname, resolve } = require('path'); const fs = require('fs'); -const { paramCase } = require('param-case'); +const { parse } = require('tsconfck'); + +const DEFAULT_TSCONFIG_OPTIONS = { + compilerOptions: {} +}; /** * @typedef {object} LoadTsconfigInit @@ -31,107 +34,22 @@ function walkParentDirs({ base, start, filename }) { return null; } -/** - * @param {ts.CompilerOptions} options - * @param {string | undefined} key - * @param {(value: string) => string} [callback] - * @returns {string | undefined} - */ -function convertEnumCompilerOptions(enumCompilerOptions, key, callback) { - if (key == null) { - return undefined; - } - const value = enumCompilerOptions[key]; - return typeof callback === 'function' ? callback(value) : value; -} - -/** - * @param {string} value - * @returns {string} - */ -function toLowerCase(value) { - return value.toLowerCase(); -} - -/** - * @param {ts.NewLineKind} newLine - * @returns {string | undefined} - */ -function normalizeNewLineOption(newLine) { - switch (newLine) { - case ts.NewLineKind.CarriageReturnLineFeed: - return 'crlf'; - case ts.NewLineKind.LineFeed: - return 'lf'; - default: - return undefined; - } -} - -/** - * @param {ts.ModuleResolutionKind} moduleResolution - * @returns {string | undefined} - */ -function normalizeModuleResolutionOption(moduleResolution) { - switch (moduleResolution) { - case ts.ModuleResolutionKind.Classic: - return 'classic'; - case ts.ModuleResolutionKind.NodeJs: - return 'node'; - case ts.ModuleResolutionKind.Node12: - return 'node12'; - case ts.ModuleResolutionKind.NodeNext: - return 'nodenext'; - default: - return undefined; - } -} - -/** - * @param {ts.CompilerOptions} options - * @returns {ts.CompilerOptions} - */ -function normalizeCompilerOptions(options) { - if (options.importsNotUsedAsValues != null) { - options.importsNotUsedAsValues = convertEnumCompilerOptions( - ts.ImportsNotUsedAsValues, - options.importsNotUsedAsValues, - toLowerCase, - ); - } - if (options.jsx != null) { - options.jsx = convertEnumCompilerOptions(ts.JsxEmit, options.jsx, paramCase); - } - if (options.module != null) { - options.module = convertEnumCompilerOptions(ts.ModuleKind, options.module, toLowerCase); - } - if (options.moduleResolution != null) { - options.moduleResolution = normalizeModuleResolutionOption(options.moduleResolution); - } - if (options.newLine != null) { - options.newLine = normalizeNewLineOption(options.newLine); - } - if (options.target != null) { - options.target = convertEnumCompilerOptions(ts.ScriptTarget, options.target, toLowerCase); - } - return options; -} - /** * @param {string | undefined} configPath * @param {LoadTsconfigInit} - * @returns {ts.CompilerOptions} + * @returns {Promise} */ -exports.loadTsconfigOptions = function (configPath, { base, start, filename }) { +exports.loadTsconfigOptions = async function (configPath, { base, start, filename }) { // throw error if `configPath` does not exist const tsconfig = configPath != null ? resolve(configPath) : walkParentDirs({ base, start, filename }); if (tsconfig == null) { - return {}; + return DEFAULT_TSCONFIG_OPTIONS; } - const content = ts.readConfigFile(tsconfig, ts.sys.readFile); - if (content.error != null || content.config == null) { - return {}; + try { + const result = await parse(tsconfig); + return result.tsconfig; + } catch (error) { + console.error(error); + throw error; } - const { options } = ts.parseJsonConfigFileContent(content.config, ts.sys, dirname(tsconfig)); - return normalizeCompilerOptions(options); }; diff --git a/yarn.lock b/yarn.lock index 89c8f6c9..337e5ad7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5512,14 +5512,6 @@ dot-case@^1.1.0: dependencies: sentence-case "^1.1.2" -dot-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" - integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== - dependencies: - no-case "^3.0.4" - tslib "^2.0.3" - dot-prop@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.1.tgz#45884194a71fc2cda71cbb4bceb3a4dd2f433ba4" @@ -9949,13 +9941,6 @@ lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2: resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= -lower-case@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" - integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== - dependencies: - tslib "^2.0.3" - lowercase-keys@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" @@ -10762,14 +10747,6 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -no-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" - integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== - dependencies: - lower-case "^2.0.2" - tslib "^2.0.3" - node-abi@^2.21.0: version "2.30.0" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.30.0.tgz#8be53bf3e7945a34eea10e0fc9a5982776cf550b" @@ -11734,14 +11711,6 @@ param-case@^1.1.0: dependencies: sentence-case "^1.1.2" -param-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" - integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== - dependencies: - dot-case "^3.0.4" - tslib "^2.0.3" - parents@^1.0.0, parents@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751" @@ -14858,6 +14827,11 @@ ts-loader@^8.3.0: micromatch "^4.0.0" semver "^7.3.4" +tsconfck@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-1.2.2.tgz#3f7ac55bcd16b8d89ff05ba8e27057f3375828c8" + integrity sha512-x5YpjOqjJnMs1EsJvQBQbrysrY32eGoZRRr5YvbN1hwlrXKc7jiphCOUrT7xbFdOWk8sh+EtMYbGPbTO8rDmcw== + tsconfig-paths-webpack-plugin@^3.2.0: version "3.5.1" resolved "https://registry.yarnpkg.com/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-3.5.1.tgz#e4dbf492a20dca9caab60086ddacb703afc2b726" @@ -14892,11 +14866,6 @@ tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.2.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== -tslib@^2.0.3: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== - tslib@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"