Skip to content

Commit

Permalink
feat: support using ESLint to lint TypeScript
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Feb 7, 2018
1 parent 364fddf commit dd04add
Show file tree
Hide file tree
Showing 20 changed files with 255 additions and 49 deletions.
11 changes: 7 additions & 4 deletions __mocks__/inquirer.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,15 @@ exports.prompt = prompts => {
expect(message).toMatch(a.message)
}

const choices = typeof prompt.choices === 'function'
? prompt.choices(answers)
: prompt.choices
if (a.choices) {
expect(prompt.choices.length).toBe(a.choices.length)
expect(choices.length).toBe(a.choices.length)
a.choices.forEach((c, i) => {
const expected = a.choices[i]
if (expected) {
expect(prompt.choices[i].name).toMatch(expected)
expect(choices[i].name).toMatch(expected)
}
})
}
Expand All @@ -56,12 +59,12 @@ exports.prompt = prompts => {

if (a.choose != null) {
expect(prompt.type === 'list' || prompt.type === 'rawList').toBe(true)
setValue(prompt.choices[a.choose].value)
setValue(choices[a.choose].value)
}

if (a.check != null) {
expect(prompt.type).toBe('checkbox')
setValue(a.check.map(i => prompt.choices[i].value))
setValue(a.check.map(i => choices[i].value))
}

if (a.confirm != null) {
Expand Down
24 changes: 24 additions & 0 deletions packages/@vue/cli-plugin-eslint/__tests__/eslintGenerator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,30 @@ test('prettier', async () => {
expect(pkg.devDependencies).toHaveProperty('@vue/eslint-config-prettier')
})

test('typescript', async () => {
const { pkg } = await generateWithPlugin([
{
id: 'eslint',
apply: require('../generator'),
options: {
config: 'prettier'
}
},
{
id: 'typescript',
apply: require('@vue/cli-plugin-typescript/generator'),
options: {}
}
])

expect(pkg.scripts.lint).toBeTruthy()
expect(pkg.eslintConfig).toEqual({
extends: ['plugin:vue/essential', '@vue/prettier', '@vue/typescript']
})
expect(pkg.devDependencies).toHaveProperty('@vue/eslint-config-prettier')
expect(pkg.devDependencies).toHaveProperty('@vue/eslint-config-typescript')
})

test('lint on save', async () => {
const { pkg } = await generateWithPlugin({
id: 'eslint',
Expand Down
27 changes: 18 additions & 9 deletions packages/@vue/cli-plugin-eslint/eslintOptions.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
module.exports = {
extensions: ['.js', '.vue'],
parserOptions: {
parser: require.resolve('babel-eslint')
},
globals: ['process'],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
module.exports = api => {
const options = {
extensions: ['.js', '.vue'],
globals: ['process'],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
}
}

if (api.hasPlugin('typescript')) {
options.extensions.push('.ts')
} else {
options.parserOptions = {
parser: require.resolve('babel-eslint')
}
}

return options
}
10 changes: 9 additions & 1 deletion packages/@vue/cli-plugin-eslint/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ module.exports = (api, { config, lintOn = [] }) => {
pkg.eslintConfig.extends.push('eslint:recommended')
}

// typescript support
if (api.hasPlugin('typescript')) {
pkg.eslintConfig.extends.push('@vue/typescript')
Object.assign(pkg.devDependencies, {
'@vue/eslint-config-typescript': '^3.0.0-alpha.9'
})
}

if (lintOn.includes('save')) {
pkg.vue = {
lintOnSave: true // eslint-loader configured in runtime plugin
Expand Down Expand Up @@ -71,7 +79,7 @@ module.exports = (api, { config, lintOn = [] }) => {
// lint & fix after create to ensure files adhere to chosen config
if (config && config !== 'base') {
api.onCreateComplete(() => {
require('./lint')(api.resolve('.'), { silent: true })
require('./lint')({ silent: true }, api)
})
}
}
4 changes: 2 additions & 2 deletions packages/@vue/cli-plugin-eslint/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = (api, { lintOnSave }) => {
if (lintOnSave) {
const options = require('./eslintOptions')
const options = require('./eslintOptions')(api)
api.chainWebpack(webpackConfig => {
webpackConfig.module
.rule('eslint')
Expand All @@ -27,6 +27,6 @@ module.exports = (api, { lintOnSave }) => {
},
details: 'For more options, see https://eslint.org/docs/user-guide/command-line-interface#options'
}, args => {
require('./lint')(api.resolve('.'), args)
require('./lint')(args, api)
})
}
5 changes: 3 additions & 2 deletions packages/@vue/cli-plugin-eslint/lint.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module.exports = function lint (cwd, args = {}) {
module.exports = function lint (args = {}, api) {
const cwd = api.resolve('.')
const { CLIEngine } = require('eslint')
const options = require('./eslintOptions')
const options = require('./eslintOptions')(api)
const { done } = require('@vue/cli-shared-utils')

const files = args._ && args._.length ? args._ : ['src', 'test']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ test('lint', async () => {
id: 'ts',
apply: require('../generator'),
options: {
lint: true,
tsLint: true,
lintOn: ['save', 'commit']
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
jest.setTimeout(10000)

const create = require('@vue/cli-test-utils/createTestProject')

test('should work', async () => {
const project = await create('ts-lint', {
plugins: {
'@vue/cli-plugin-eslint': {
config: 'prettier'
},
'@vue/cli-plugin-typescript': {
classComponent: true
}
}
})
const { read, write, run } = project
const main = await read('src/main.ts')
expect(main).toMatch(';')
const app = await read('src/App.vue')
expect(main).toMatch(';')
// remove semicolons
const updatedMain = main.replace(/;/g, '')
await write('src/main.ts', updatedMain)
// for Vue file, only remove semis in script section
const updatedApp = app.replace(/<script(.|\n)*\/script>/, $ => {
return $.replace(/;/g, '')
})
await write('src/App.vue', updatedApp)
// lint
await run('vue-cli-service lint')
expect(await read('src/main.ts')).toMatch(';')

const lintedApp = await read('src/App.vue')
expect(lintedApp).toMatch(';')
// test if ESLint is fixing vue files properly
expect(lintedApp).toBe(app)
})
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ test('should work', async () => {
const project = await create('ts-lint', {
plugins: {
'@vue/cli-plugin-typescript': {
lint: true
tsLint: true
}
}
})
Expand Down
6 changes: 2 additions & 4 deletions packages/@vue/cli-plugin-typescript/generator/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = (api, {
classComponent,
lint,
tsLint,
lintOn = [],
experimentalCompileTsWithBabel
}) => {
Expand Down Expand Up @@ -46,7 +46,7 @@ module.exports = (api, {
}
}

if (lint) {
if (tsLint && !api.hasPlugin('eslint')) {
api.extendPackage({
scripts: {
lint: 'vue-cli-service lint'
Expand Down Expand Up @@ -98,8 +98,6 @@ module.exports = (api, {
})
}

// TODO cater to e2e test plugins

api.render('./template', {
isTest: process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG,
hasMocha,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<%_ if (options.lint) { _%>
<%_ if (options.tsLint) { _%>
{
"defaultSeverity": "warning",
"extends": [
Expand Down
26 changes: 14 additions & 12 deletions packages/@vue/cli-plugin-typescript/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,18 @@ module.exports = (api, {
}])
})

api.registerCommand('lint', {
descriptions: 'lint source files with TSLint',
usage: 'vue-cli-service lint [options] [...files]',
options: {
'--format [formatter]': 'specify formatter (default: codeFrame)',
'--no-fix': 'do not fix errors',
'--formatters-dir [dir]': 'formatter directory',
'--rules-dir [dir]': 'rules directory'
}
}, args => {
return require('./lib/tslint')(args, api)
})
if (!api.hasPlugin('eslint')) {
api.registerCommand('lint', {
descriptions: 'lint source files with TSLint',
usage: 'vue-cli-service lint [options] [...files]',
options: {
'--format [formatter]': 'specify formatter (default: codeFrame)',
'--no-fix': 'do not fix errors',
'--formatters-dir [dir]': 'formatter directory',
'--rules-dir [dir]': 'rules directory'
}
}, args => {
return require('./lib/tslint')(args, api)
})
}
}
57 changes: 55 additions & 2 deletions packages/@vue/cli/lib/promptModules/__tests__/typescript.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const assertPromptModule = require('@vue/cli-test-utils/assertPromptModule')
const moduleToTest = require('../typescript')
const linterModule = require('../linter')

test('should work', async () => {
test('with TSLint', async () => {
const expectedPrompts = [
{
message: 'features',
Expand All @@ -21,6 +21,11 @@ test('should work', async () => {
message: 'Use Babel',
confirm: true
},
{
message: 'Pick a linter / formatter',
choices: ['TSLint', 'error prevention', 'Airbnb', 'Standard', 'Prettier'],
choose: [0]
},
{
message: 'Pick additional lint features',
choices: ['on save', 'on commit'],
Expand All @@ -32,7 +37,7 @@ test('should work', async () => {
plugins: {
'@vue/cli-plugin-typescript': {
classComponent: true,
lint: true,
tsLint: true,
lintOn: ['save', 'commit'],
useTsWithBabel: true
}
Expand All @@ -46,3 +51,51 @@ test('should work', async () => {
{ plguinsOnly: true }
)
})

test('with ESLint', async () => {
const expectedPrompts = [
{
message: 'features',
choices: ['TypeScript', 'Linter'],
check: [0, 1]
},
{
message: 'Use class-style component',
confirm: true
},
{
message: 'Use Babel',
confirm: true
},
{
message: 'Pick a linter / formatter',
choices: ['TSLint', 'error prevention', 'Airbnb', 'Standard', 'Prettier'],
choose: [2]
},
{
message: 'Pick additional lint features',
choices: ['on save', 'on commit'],
check: [0, 1]
}
]

const expectedOptions = {
plugins: {
'@vue/cli-plugin-eslint': {
config: 'airbnb',
lintOn: ['save', 'commit']
},
'@vue/cli-plugin-typescript': {
classComponent: true,
useTsWithBabel: true
}
}
}

await assertPromptModule(
[moduleToTest, linterModule],
expectedPrompts,
expectedOptions,
{ plguinsOnly: true }
)
})
19 changes: 12 additions & 7 deletions packages/@vue/cli/lib/promptModules/linter.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@ module.exports = cli => {

cli.injectPrompt({
name: 'eslintConfig',
when: answers => (
answers.features.includes('linter') &&
!answers.features.includes('ts')
),
when: answers => answers.features.includes('linter'),
type: 'list',
message: 'Pick a linter / formatter config:',
choices: [
choices: answers => [
...(
answers.features.includes('ts')
? [{
name: `TSLint`,
value: 'tslint',
short: 'TSLint'
}]
: []
),
{
name: 'ESLint with error prevention only',
value: 'base',
Expand Down Expand Up @@ -58,8 +64,7 @@ module.exports = cli => {
})

cli.onPromptComplete((answers, options) => {
if (answers.features.includes('linter') &&
!answers.features.includes('ts')) {
if (answers.features.includes('linter') && answers.eslintConfig !== 'tslint') {
options.plugins['@vue/cli-plugin-eslint'] = {
config: answers.eslintConfig,
lintOn: answers.lintOn
Expand Down
4 changes: 2 additions & 2 deletions packages/@vue/cli/lib/promptModules/typescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ module.exports = cli => {
const tsOptions = {
classComponent: answers.tsClassComponent
}
if (answers.features.includes('linter')) {
tsOptions.lint = true
if (answers.eslintConfig === 'tslint') {
tsOptions.tsLint = true
tsOptions.lintOn = answers.lintOn
}
if (answers.useTsWithBabel) {
Expand Down
2 changes: 2 additions & 0 deletions packages/@vue/eslint-config-typescript/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__tests__/
__mocks__/
3 changes: 3 additions & 0 deletions packages/@vue/eslint-config-typescript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @vue/eslint-config-typescript

> eslint-config-typescript for vue-cli
Loading

0 comments on commit dd04add

Please sign in to comment.