diff --git a/distributions/nuxt-ts/README.md b/distributions/nuxt-ts/README.md new file mode 100644 index 0000000000..21a22a2ec4 --- /dev/null +++ b/distributions/nuxt-ts/README.md @@ -0,0 +1,3 @@ +# nuxt-ts + +> Nuxt With Runtime Typescript Support diff --git a/distributions/nuxt-ts/bin/nuxt-ts.js b/distributions/nuxt-ts/bin/nuxt-ts.js new file mode 100644 index 0000000000..9439bb94c1 --- /dev/null +++ b/distributions/nuxt-ts/bin/nuxt-ts.js @@ -0,0 +1,11 @@ +#!/usr/bin/env node + +const { register } = require('ts-node') + +// globally indicate we are running in ts mode +process.env.NUXT_TS = 'true' + +// https://github.com/TypeStrong/ts-node +register() + +require('@nuxt/cli').run() diff --git a/distributions/nuxt-ts/package.js b/distributions/nuxt-ts/package.js new file mode 100644 index 0000000000..7ec679a347 --- /dev/null +++ b/distributions/nuxt-ts/package.js @@ -0,0 +1,23 @@ +export default { + build: false, + hooks: { + async 'build:done'(pkg) { + const mono = pkg.load('../..') + const nuxt = pkg.load('../nuxt') + + await pkg.copyFilesFrom(mono, [ + 'LICENSE' + ]) + + pkg.copyFieldsFrom(nuxt, [ + 'license', + 'repository', + 'contributors', + 'keywords', + 'collective' + ]) + + await pkg.writePackage() + } + } +} diff --git a/distributions/nuxt-ts/package.json b/distributions/nuxt-ts/package.json new file mode 100644 index 0000000000..5fe6034c56 --- /dev/null +++ b/distributions/nuxt-ts/package.json @@ -0,0 +1,70 @@ +{ + "name": "nuxt-ts", + "version": "2.3.4", + "description": "Nuxt With Runtime Typescript Support", + "keywords": [ + "nuxt", + "nuxt.js", + "nuxtjs", + "ssr", + "vue", + "vue isomorphic", + "vue server side", + "vue ssr", + "vue universal", + "vue versatile", + "vue.js", + "vuejs" + ], + "homepage": "https://github.com/nuxt/nuxt.js#readme", + "repository": "nuxt/nuxt.js", + "license": "MIT", + "contributors": [ + { + "name": "Sebastien Chopin (@Atinux)" + }, + { + "name": "Alexandre Chopin (@alexchopin)" + }, + { + "name": "Pooya Parsa (@pi0)" + }, + { + "name": "Clark Du (@clarkdo)" + }, + { + "name": "Jonas Galvez (@galvez)" + }, + { + "name": "Alexander Lichter (@manniL)" + } + ], + "files": [ + "bin" + ], + "bin": { + "nuxt-ts": "bin/nuxt-ts.js", + "nuxts": "bin/nuxt-ts.js" + }, + "dependencies": { + "@nuxt/builder": "2.3.4", + "@nuxt/cli": "2.3.4", + "@nuxt/core": "2.3.4", + "@nuxt/generator": "2.3.4", + "@nuxt/opencollective": "^0.2.1", + "@nuxt/webpack": "2.3.4", + "fork-ts-checker-webpack-plugin": "^0.5.2", + "ts-loader": "^5.3.2", + "ts-node": "^7.0.1", + "tslint": "^5.12.0", + "typescript": "^3.2.2" + }, + "engines": { + "node": ">=6.0.0", + "npm": ">=3.0.0" + }, + "collective": { + "url": "https://opencollective.com/nuxtjs", + "logoUrl": "https://opencollective.com/nuxtjs/logo.txt?reverse=true&variant=variant2" + } +} diff --git a/jest.config.js b/jest.config.js index 8927536272..fa32e37f47 100644 --- a/jest.config.js +++ b/jest.config.js @@ -33,11 +33,13 @@ module.exports = { ], transform: { + '^.+\\.ts$': 'ts-jest', '^.+\\.js$': 'babel-jest', '.*\\.(vue)$': 'vue-jest' }, moduleFileExtensions: [ + 'ts', 'js', 'json' ], diff --git a/package.json b/package.json index 06f6ef3ed6..e006f2c622 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "rollup-plugin-node-resolve": "^4.0.0", "rollup-plugin-replace": "^2.1.0", "sort-package-json": "^1.17.1", + "ts-jest": "^23.10.5", "ts-loader": "^5.3.2", "tslint": "^5.12.0", "typescript": "^3.2.2", diff --git a/packages/cli/src/options/common.js b/packages/cli/src/options/common.js index d42878ae8d..c4eb88b565 100644 --- a/packages/cli/src/options/common.js +++ b/packages/cli/src/options/common.js @@ -1,5 +1,7 @@ import { normalizeArg } from '../utils' +const defaultConfigFile = `nuxt.config${process.env.NUXT_TS === 'true' ? '.ts' : '.js'}` + export default { spa: { alias: 's', @@ -14,8 +16,8 @@ export default { 'config-file': { alias: 'c', type: 'string', - default: 'nuxt.config.js', - description: 'Path to Nuxt.js config file (default: `nuxt.config.js`)' + default: defaultConfigFile, + description: `Path to Nuxt.js config file (default: \`${defaultConfigFile}\`)` }, modern: { alias: 'm', diff --git a/packages/config/src/config/build.js b/packages/config/src/config/build.js index 1b0b5de7fa..8b0919d37a 100644 --- a/packages/config/src/config/build.js +++ b/packages/config/src/config/build.js @@ -54,7 +54,7 @@ export default () => ({ }, vueStyle: {} }, - useForkTsChecker: false, + useForkTsChecker: process.env.NUXT_TS === 'true', styleResources: {}, plugins: [], terser: {}, diff --git a/packages/core/src/resolver.js b/packages/core/src/resolver.js index 7dc898379c..048c208e41 100644 --- a/packages/core/src/resolver.js +++ b/packages/core/src/resolver.js @@ -105,14 +105,18 @@ export default class Resolver { requireModule(path, { esm, alias, intropDefault } = {}) { let resolvedPath = path let requiredModule - - const errors = [] + let lastError // Try to resolve path try { resolvedPath = this.resolvePath(path, { alias }) } catch (e) { - errors.push(e) + lastError = e + } + + // Disable esm for ts files by default + if (esm === undefined && /.ts$/.test(resolvedPath)) { + esm = false } // Try to require @@ -123,7 +127,7 @@ export default class Resolver { requiredModule = this.esm(resolvedPath) } } catch (e) { - errors.push(e) + lastError = e } // Introp default @@ -132,8 +136,8 @@ export default class Resolver { } // Throw error if failed to require - if (requiredModule === undefined && errors.length) { - throw errors + if (requiredModule === undefined && lastError) { + throw lastError } return requiredModule diff --git a/packages/server/src/server.js b/packages/server/src/server.js index 4370cce259..1e79df1841 100644 --- a/packages/server/src/server.js +++ b/packages/server/src/server.js @@ -125,9 +125,9 @@ export default class Server { } // Add user provided middleware - this.options.serverMiddleware.forEach((m) => { + for (const m of this.options.serverMiddleware) { this.useMiddleware(m) - }) + } const { fallback } = this.options.render if (fallback) { @@ -188,11 +188,11 @@ export default class Server { handler = requiredModuleFromHandlerPath.handler || requiredModuleFromHandlerPath } catch (err) { + consola.error(err) + // Throw error in production mode if (!this.options.dev) { - throw err[0] + throw err } - // Only warn missing file in development - consola.warn(err[0]) } } diff --git a/test/fixtures/typescript/modules/module.ts b/test/fixtures/typescript/modules/module.ts new file mode 100644 index 0000000000..ead516c976 --- /dev/null +++ b/test/fixtures/typescript/modules/module.ts @@ -0,0 +1 @@ +export default () => {} diff --git a/test/fixtures/typescript/nuxt.config.js b/test/fixtures/typescript/nuxt.config.js deleted file mode 100644 index f0a5ab50f2..0000000000 --- a/test/fixtures/typescript/nuxt.config.js +++ /dev/null @@ -1,10 +0,0 @@ -import consola from 'consola' - -export default { - build: { - useForkTsChecker: { - logger: consola - } - }, - plugins: ['~/plugins/plugin'] -} diff --git a/test/fixtures/typescript/nuxt.config.ts b/test/fixtures/typescript/nuxt.config.ts new file mode 100644 index 0000000000..ab6b0bc663 --- /dev/null +++ b/test/fixtures/typescript/nuxt.config.ts @@ -0,0 +1,13 @@ +const config: any = { + modules: [ + '~/modules/module' + ], + plugins: [ + '~/plugins/plugin' + ], + serverMiddleware: [ + '~/server-middleware/test.ts' + ] +} + +export default config diff --git a/test/fixtures/typescript/server-middleware/test.ts b/test/fixtures/typescript/server-middleware/test.ts new file mode 100644 index 0000000000..cdb0ac4668 --- /dev/null +++ b/test/fixtures/typescript/server-middleware/test.ts @@ -0,0 +1,7 @@ +export default { + path: '/api/test', + handler(_, res) { + const message: String = 'Works!' + res.end(message) + } +} diff --git a/test/fixtures/typescript/typescript.test.js b/test/fixtures/typescript/typescript.test.js index 0d352b49dd..8591a68ad4 100644 --- a/test/fixtures/typescript/typescript.test.js +++ b/test/fixtures/typescript/typescript.test.js @@ -1,3 +1,5 @@ import { buildFixture } from '../../utils/build' +process.env.NUXT_TS = 'true' buildFixture('typescript') +delete process.env.NUXT_TS diff --git a/test/unit/typescript.test.js b/test/unit/typescript.test.js index b05e3b6e22..d83c4971a8 100644 --- a/test/unit/typescript.test.js +++ b/test/unit/typescript.test.js @@ -1,12 +1,17 @@ -import { loadFixture, getPort, Nuxt } from '../utils' - -let nuxt = null +import { loadFixture, getPort, Nuxt, rp } from '../utils' describe('typescript', () => { + let nuxt + let port + const url = route => 'http://localhost:' + port + route + beforeAll(async () => { + process.env.NUXT_TS = 'true' const options = await loadFixture('typescript') + delete process.env.NUXT_TS nuxt = new Nuxt(options) - const port = await getPort() + await nuxt.ready() + port = await getPort() await nuxt.server.listen(port, '0.0.0.0') }) @@ -30,6 +35,15 @@ describe('typescript', () => { expect(html).toContain('
Contact Page
') }) + test('/api/test', async () => { + const html = await rp(url('/api/test')) + expect(html).toContain('Works!') + }) + + test('TS module successfully required', () => { + expect(nuxt.moduleContainer.requiredModules).toHaveProperty('~/modules/module') + }) + // Close server and ask nuxt to stop listening to file changes afterAll(async () => { await nuxt.close() diff --git a/test/utils/nuxt.js b/test/utils/nuxt.js index fbc44dd99e..bb4aed3ea6 100644 --- a/test/utils/nuxt.js +++ b/test/utils/nuxt.js @@ -12,7 +12,7 @@ export * from '../../packages/utils/src/index' export const loadFixture = async function (fixture, overrides) { const rootDir = path.resolve(__dirname, '..', 'fixtures', fixture) - const configFile = path.resolve(rootDir, 'nuxt.config.js') + const configFile = path.resolve(rootDir, `nuxt.config${process.env.NUXT_TS === 'true' ? '.ts' : '.js'}`) let config = fs.existsSync(configFile) ? (await import(`../fixtures/${fixture}/nuxt.config`)).default : {} if (typeof config === 'function') { diff --git a/yarn.lock b/yarn.lock index 5c834dfec3..8f7f9a16d2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2483,6 +2483,13 @@ browserslist@^4.3.5: electron-to-chromium "^1.3.86" node-releases "^1.0.5" +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + bser@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719" @@ -2490,7 +2497,7 @@ bser@^2.0.0: dependencies: node-int64 "^0.4.0" -buffer-from@^1.0.0: +buffer-from@1.x, buffer-from@^1.0.0, buffer-from@^1.1.0: version "1.1.1" resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== @@ -3893,7 +3900,7 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -diff@^3.2.0: +diff@^3.1.0, diff@^3.2.0: version "3.5.0" resolved "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== @@ -4702,7 +4709,7 @@ fast-glob@^2.0.2: merge2 "^1.2.3" micromatch "^3.1.10" -fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= @@ -6622,6 +6629,13 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= +json5@2.x, json5@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850" + integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ== + dependencies: + minimist "^1.2.0" + json5@^0.5.0, json5@^0.5.1: version "0.5.1" resolved "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" @@ -6634,13 +6648,6 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850" - integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ== - dependencies: - minimist "^1.2.0" - jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -7113,6 +7120,11 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" +make-error@1.x, make-error@^1.1.1: + version "1.3.5" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" + integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== + make-fetch-happen@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-4.0.1.tgz#141497cb878f243ba93136c83d8aba12c216c083" @@ -7431,7 +7443,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@0.5.1, mkdirp@0.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= @@ -8245,7 +8257,7 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= -path-parse@^1.0.5: +path-parse@^1.0.5, path-parse@^1.0.6: version "1.0.6" resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== @@ -9831,6 +9843,13 @@ resolve@1.1.7: resolved "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= +resolve@1.x: + version "1.9.0" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz#a14c6fdfa8f92a7df1d996cb7105fa744658ea06" + integrity sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ== + dependencies: + path-parse "^1.0.6" + resolve@^1.1.6, resolve@^1.1.7, resolve@^1.2.0, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.6.0, resolve@^1.8.1: version "1.8.1" resolved "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" @@ -10061,7 +10080,7 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.0.1, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.0.1, semver@^5.3.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: version "5.6.0" resolved "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== @@ -10992,6 +11011,21 @@ tryer@^1.0.0: resolved "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== +ts-jest@^23.10.5: + version "23.10.5" + resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-23.10.5.tgz#cdb550df4466a30489bf70ba867615799f388dd5" + integrity sha512-MRCs9qnGoyKgFc8adDEntAOP64fWK1vZKnOYU1o2HxaqjdJvGqmkLCPCnVq1/If4zkUmEjKPnCiUisTrlX2p2A== + dependencies: + bs-logger "0.x" + buffer-from "1.x" + fast-json-stable-stringify "2.x" + json5 "2.x" + make-error "1.x" + mkdirp "0.x" + resolve "1.x" + semver "^5.5" + yargs-parser "10.x" + ts-loader@^5.3.2: version "5.3.2" resolved "https://registry.npmjs.org/ts-loader/-/ts-loader-5.3.2.tgz#31d10be522bedfac8ee4c20c735e05a9bd772faf" @@ -11003,6 +11037,20 @@ ts-loader@^5.3.2: micromatch "^3.1.4" semver "^5.0.1" +ts-node@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz#9562dc2d1e6d248d24bc55f773e3f614337d9baf" + integrity sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw== + dependencies: + arrify "^1.0.0" + buffer-from "^1.1.0" + diff "^3.1.0" + make-error "^1.1.1" + minimist "^1.2.0" + mkdirp "^0.5.1" + source-map-support "^0.5.6" + yn "^2.0.0" + tsconfig@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" @@ -11843,7 +11891,7 @@ yallist@^3.0.0, yallist@^3.0.2: resolved "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" integrity sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k= -yargs-parser@^10.1.0: +yargs-parser@10.x, yargs-parser@^10.1.0: version "10.1.0" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ== @@ -11909,3 +11957,8 @@ yauzl@2.4.1: integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU= dependencies: fd-slicer "~1.0.1" + +yn@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" + integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=