From 6ffc5c57923922821d52b0cf5186f27f273b7d33 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Fri, 29 Mar 2019 23:49:30 +0430 Subject: [PATCH] feat(typescript): detect typescript based on `tsconfig.json` (#5412) --- package.json | 25 ++++++----- packages/cli/package.json | 1 + packages/cli/src/command.js | 8 +++- packages/cli/src/imports.js | 4 ++ packages/cli/src/utils/typescript.js | 53 ++++++++++++++++++----- packages/cli/test/unit/typescript.test.js | 41 ++++++++++++++++++ packages/typescript/package.json | 1 - packages/typescript/src/index.js | 26 +++-------- packages/typescript/test/setup.test.js | 30 +++++-------- 9 files changed, 124 insertions(+), 65 deletions(-) create mode 100644 packages/cli/test/unit/typescript.test.js diff --git a/package.json b/package.json index 2f722af95b..38441175ec 100644 --- a/package.json +++ b/package.json @@ -1,29 +1,34 @@ { + "name": "nuxt.js", "private": true, + "repository": { + "type": "git", + "url": "git+https://github.com/nuxt/nuxt.js.git" + }, "workspaces": [ "packages/*", "distributions/*" ], "scripts": { "build": "yarn clean && yarn pkg", - "pkg": "node -r esm ./scripts/package", "clean": "yarn clean:build && yarn clean:examples && yarn clean:test", "clean:build": "rimraf distributions/*/dist packages/*/dist", "clean:examples": "rimraf examples/*/dist examples/*/.nuxt", "clean:test": "rimraf test/fixtures/*/dist test/fixtures/*/.nuxt*/", - "dev": "node -r esm ./scripts/dev.js", "coverage": "codecov", + "dev": "node -r esm ./scripts/dev.js", + "postinstall": "lerna link && yarn dev", "lint": "eslint --ext .js,.mjs,.vue .", "lint:app": "eslint-multiplexer eslint --ignore-path packages/vue-app/template/.eslintignore 'test/fixtures/!(missing-plugin)/.nuxt!(-dev)/**' | eslint-multiplexer -b", "nuxt": "node -r esm ./packages/cli/bin/nuxt-cli.js", + "pkg": "node -r esm ./scripts/package", + "release": "./scripts/release", "test": "yarn test:fixtures && yarn test:unit && yarn test:types", - "test:fixtures": "jest test/fixtures --forceExit", "test:e2e": "jest -i test/e2e --forceExit", + "test:fixtures": "jest test/fixtures --forceExit", "test:lint": "yarn lint", - "test:unit": "jest test/unit packages --forceExit", "test:types": "tsc -p test/types", - "postinstall": "lerna link && yarn dev", - "release": "./scripts/release" + "test:unit": "jest test/unit packages --forceExit" }, "devDependencies": { "@babel/core": "^7.4.0", @@ -79,14 +84,10 @@ "sort-package-json": "^1.22.1", "ts-jest": "^24.0.0", "ts-loader": "^5.3.3", + "ts-node": "^8.0.3", "tslint": "^5.14.0", "typescript": "^3.4.1", "vue-jest": "^4.0.0-beta.2", "vue-property-decorator": "^8.1.0" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/nuxt/nuxt.js.git" - }, - "name": "nuxt.js" + } } diff --git a/packages/cli/package.json b/packages/cli/package.json index 9855b19e5f..5293a97723 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -20,6 +20,7 @@ "esm": "^3.2.22", "execa": "^1.0.0", "exit": "^0.1.2", + "fs-extra": "^7.0.1", "minimist": "^1.2.0", "opener": "1.5.1", "pretty-bytes": "^5.1.0", diff --git a/packages/cli/src/command.js b/packages/cli/src/command.js index 296cbbca09..576d579e0e 100644 --- a/packages/cli/src/command.js +++ b/packages/cli/src/command.js @@ -6,7 +6,7 @@ import { name, version } from '../package.json' import { loadNuxtConfig, forceExit } from './utils' import { indent, foldLines, colorize } from './utils/formatting' import { startSpaces, optionSpaces, forceExitTimeout } from './utils/constants' -import { detectAndSetupTypeScriptSupport } from './utils/typescript' +import { detectTypeScript } from './utils/typescript' import * as imports from './imports' export default class NuxtCommand { @@ -92,7 +92,11 @@ export default class NuxtCommand { async getNuxtConfig(extraOptions = {}) { const rootDir = path.resolve(this.argv._[0] || '.') - extraOptions._typescript = await detectAndSetupTypeScriptSupport(rootDir, { transpileOnly: this.cmd.name === 'start' }) + + // Typescript support + extraOptions._typescript = await detectTypeScript(rootDir, { + transpileOnly: this.cmd.name === 'start' + }) const config = await loadNuxtConfig(this.argv) const options = Object.assign(config, extraOptions) diff --git a/packages/cli/src/imports.js b/packages/cli/src/imports.js index cae3174aa6..b68f1bc38c 100644 --- a/packages/cli/src/imports.js +++ b/packages/cli/src/imports.js @@ -29,4 +29,8 @@ export const builder = () => _import('@nuxt/builder') export const webpack = () => _import('@nuxt/webpack') export const generator = () => _import('@nuxt/generator') export const core = () => _import('@nuxt/core') + +export const tsNode = () => _import('ts-node') +export const nuxtTypescript = () => _import('@nuxt/typescript') + export const importModule = _import diff --git a/packages/cli/src/utils/typescript.js b/packages/cli/src/utils/typescript.js index 384d5bcdb0..da91bacd15 100644 --- a/packages/cli/src/utils/typescript.js +++ b/packages/cli/src/utils/typescript.js @@ -1,20 +1,49 @@ import path from 'path' -import consola from 'consola' +import fs from 'fs-extra' +import * as imports from '../imports' -export async function detectAndSetupTypeScriptSupport(rootDir, options = {}) { +async function registerTSNode(tsConfigPath, options) { + const { register } = await imports.tsNode() + + // https://github.com/TypeStrong/ts-node + register({ + project: tsConfigPath, + compilerOptions: { + module: 'commonjs' + }, + ...options + }) +} + +async function getNuxtTypeScript() { try { - const { setup } = require('@nuxt/typescript') - - consola.info('Initializing typeScript support') - await setup(path.resolve(rootDir, 'tsconfig.json'), options) - consola.success('TypeScript support enabled') - } catch (e) { - if (e.code === 'MODULE_NOT_FOUND') { - return false - } else { - throw (e) + return await imports.nuxtTypescript() + } catch (error) { + if (error.code !== 'MODULE_NOT_FOUND') { + throw (error) } } +} + +export async function detectTypeScript(rootDir, options = {}) { + // Check if tsconfig.json exists in project rootDir + const tsConfigPath = path.resolve(rootDir, 'tsconfig.json') + + // Skip if tsconfig.json not exists + if (!await fs.exists(tsConfigPath)) { + return false + } + + // Register runtime support + await registerTSNode(tsConfigPath, options) + + // Try to load @nuxt/typescript + const nuxtTypeScript = await getNuxtTypeScript() + + // If exists do additional setup + if (nuxtTypeScript) { + await nuxtTypeScript.setupDefaults(tsConfigPath) + } return true } diff --git a/packages/cli/test/unit/typescript.test.js b/packages/cli/test/unit/typescript.test.js new file mode 100644 index 0000000000..6ca0caf07d --- /dev/null +++ b/packages/cli/test/unit/typescript.test.js @@ -0,0 +1,41 @@ +import { resolve } from 'path' +import { mkdirp, writeFile, remove } from 'fs-extra' +import { register } from 'ts-node' +import { detectTypeScript } from '../../src/utils/typescript' + +jest.mock('ts-node') + +describe('Typescript Support', () => { + const rootDir = 'tmp' + const rootDir2 = 'tmp2' + const tsConfigPath = resolve(rootDir, 'tsconfig.json') + + beforeAll(async () => { + await mkdirp(rootDir) + await mkdirp(rootDir2) + await writeFile(tsConfigPath, '{}', 'utf-8') + }) + + test('detectTypeScript detects and registers runtime', async () => { + register.mockReset() + await detectTypeScript(rootDir) + expect(register).toHaveBeenCalledTimes(1) + expect(register).toHaveBeenCalledWith({ + project: tsConfigPath, + compilerOptions: { + module: 'commonjs' + } + }) + }) + + test('detectTypeScript skips rootDir without tsconfig.json', async () => { + register.mockReset() + await detectTypeScript(rootDir2) + expect(register).toHaveBeenCalledTimes(0) + }) + + afterAll(async () => { + await remove(rootDir) + await remove(rootDir2) + }) +}) diff --git a/packages/typescript/package.json b/packages/typescript/package.json index 82a4dda50d..024fd955eb 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -25,7 +25,6 @@ "fork-ts-checker-webpack-plugin": "^1.0.1", "fs-extra": "^7.0.1", "ts-loader": "^5.3.3", - "ts-node": "^8.0.3", "typescript": "^3.4.1" }, "publishConfig": { diff --git a/packages/typescript/src/index.js b/packages/typescript/src/index.js index a196053fbc..de06ec73b7 100644 --- a/packages/typescript/src/index.js +++ b/packages/typescript/src/index.js @@ -1,5 +1,4 @@ -import { existsSync, writeJSON } from 'fs-extra' -import { register } from 'ts-node' +import { exists, readFile, writeJSON } from 'fs-extra' import consola from 'consola' export const defaultTsJsonConfig = { @@ -35,26 +34,15 @@ export const defaultTsJsonConfig = { } } -let _setup = false +export async function setupDefaults(tsConfigPath) { + let contents = '' -export async function setup(tsConfigPath, options = {}) { - if (_setup) { - return + if (await exists(tsConfigPath)) { + contents = await readFile(tsConfigPath, 'utf-8') } - _setup = true - // Only create a default `tsconfig.json` if it doesn't exist - if (!existsSync(tsConfigPath)) { - consola.info(`Generating tsconfig.json (${tsConfigPath})`) + if (!contents || contents === '{}') { + consola.info(`Generating ${tsConfigPath.replace(process.cwd(), '')}`) await writeJSON(tsConfigPath, defaultTsJsonConfig, { spaces: 2 }) } - - // https://github.com/TypeStrong/ts-node - register({ - project: tsConfigPath, - compilerOptions: { - module: 'commonjs' - }, - ...options - }) } diff --git a/packages/typescript/test/setup.test.js b/packages/typescript/test/setup.test.js index fb7b3db83a..34d876c904 100644 --- a/packages/typescript/test/setup.test.js +++ b/packages/typescript/test/setup.test.js @@ -1,39 +1,31 @@ import { resolve } from 'path' -import { mkdirp, readJSON, remove } from 'fs-extra' -import { register } from 'ts-node' -import { defaultTsJsonConfig, setup as setupTypeScript } from '@nuxt/typescript' - -jest.mock('ts-node') +import { mkdirp, readJSON, writeFile, remove, readFile } from 'fs-extra' +import { defaultTsJsonConfig, setupDefaults } from '@nuxt/typescript' describe('typescript setup', () => { const rootDir = 'tmp' const tsConfigPath = resolve(rootDir, 'tsconfig.json') beforeAll(async () => { - // We're assuming that rootDir provided to setupTypeScript is existing so we create the tested one await mkdirp(rootDir) - await setupTypeScript(tsConfigPath) + await writeFile(tsConfigPath, '{}', 'utf-8') }) - test('tsconfig.json has been created with defaults', async () => { + test('Create tsconfig.json with defaults', async () => { + await setupDefaults(tsConfigPath) expect(await readJSON(tsConfigPath)).toEqual(defaultTsJsonConfig) }) - test('ts-node has been registered once', async () => { - // Call setupTypeScript a second time to test guard - await setupTypeScript(tsConfigPath) + test('Do not override tsconfig.json', async () => { + const fooJSON = '{ "foo": 123 }' + await writeFile(tsConfigPath, fooJSON, 'utf-8') - expect(register).toHaveBeenCalledTimes(1) - expect(register).toHaveBeenCalledWith({ - project: tsConfigPath, - compilerOptions: { - module: 'commonjs' - } - }) + await setupDefaults(tsConfigPath) + + expect(await readFile(tsConfigPath, 'utf-8')).toEqual(fooJSON) }) afterAll(async () => { - // Clean workspace by removing the temporary folder (and the generated tsconfig.json at the same time) await remove(rootDir) }) })