Skip to content

Commit

Permalink
Add Flow support to Vue files (#1103)
Browse files Browse the repository at this point in the history
* Install eslint-plugin-flowtype-errors

* Add flow:vue NPM script

* Support Flowtype syntax in esbuild Vue plugin

* Fix a JsDoc comment

* Remove unused code in esbuild Vue plugin
  • Loading branch information
snowteamer authored Oct 28, 2021
1 parent 4accc97 commit 53864ab
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 50 deletions.
3 changes: 2 additions & 1 deletion Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,8 @@ module.exports = (grunt) => {
// This map's keys will be relative Vue file paths without leading dot,
// while its values will be corresponding compiled JS strings.
cache: new Map(),
debug: false
debug: false,
flowtype: flowRemoveTypesPluginOptions
}

grunt.initConfig({
Expand Down
52 changes: 52 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"eslintfix": "eslint \"**/*.{js,vue}\" --fix",
"i18n": "node scripts/i18n.js",
"flow": "flow",
"flow:vue": "eslint --cache --plugin flowtype-errors --rule 'flowtype-errors/show-errors: error' './frontend/views/**/*.vue'",
"double": "PORT_SHIFT=1 grunt dev",
"stylelint": "stylelint 'frontend/**/*.{css,scss,vue}' --fix",
"docker": "./scripts/docker.sh"
Expand Down Expand Up @@ -137,6 +138,7 @@
"eslint-config-standard": "16.0.2",
"eslint-plugin-cypress": "2.11.3",
"eslint-plugin-flowtype": "5.7.2",
"eslint-plugin-flowtype-errors": "4.4.0",
"eslint-plugin-import": "2.22.1",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "4.2.1",
Expand Down
71 changes: 22 additions & 49 deletions scripts/esbuild-plugins/vue-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,21 @@
const { readFile } = require('fs').promises
const { relative } = require('path')

// https://github.com/vuejs/vue-component-compiler#api
const componentCompiler = require('@vue/component-compiler')
const flowRemoveTypes = require('flow-remove-types')

const { createAliasReplacer } = require('./utils')

/**
* @param {Object} [options.aliases]
* @param {Map} [options.cache]
* @param {boolean} [options.debug]
* @param {Object} [options.flowtype] Options for `flow-remove-types`.
* Leave it out to disable Flowtype syntax support.
* @returns {Object}
*/
module.exports = ({ aliases = null, cache = null, debug = false } = {}) => {
module.exports = ({ aliases = null, cache = null, debug = false, flowtype = null } = {}) => {
const aliasReplacer = aliases ? createAliasReplacer(aliases) : null

return {
Expand All @@ -31,7 +35,7 @@ module.exports = ({ aliases = null, cache = null, debug = false } = {}) => {
.then(source => aliasReplacer ? aliasReplacer({ path, source }) : source)

if (debug) console.log('vue plugin: compiling', filename)
const { result /*, usedFiles */ } = await compile({ filename, source })
const result = await compile({ filename, source, options: { flowtype } })

if (cache) cache.set(filename, result)
return result
Expand All @@ -40,57 +44,30 @@ module.exports = ({ aliases = null, cache = null, debug = false } = {}) => {
}
}

let requireDepth = 0
let usedFiles = new Set()

editModule('fs', (fs) => {
fs.readFileSync = new Proxy(fs.readFileSync, {
apply (target, thisArg, args) {
if (usedFiles && requireDepth === 0) usedFiles.add(args[0])
return Reflect.apply(target, thisArg, args)
}
})
})

editModule('module', (mod) => {
mod.prototype.require = new Proxy(mod.prototype.require, {
apply (target, thisArg, args) {
requireDepth++
try {
return Reflect.apply(target, thisArg, args)
} finally {
requireDepth--
}
}
})
})

const compiler = componentCompiler.createDefaultCompiler()
const compile = ({ filename, source }) => {
usedFiles = new Set()

const compile = ({ filename, source, options }) => {
try {
if (/^\s*$/.test(source)) {
throw new Error('File is empty')
}
const result = compiler.compileToDescriptor(filename, source)
const resultErrors = combineErrors(result.template, ...result.styles)
if (resultErrors.length > 0) {
return { result: { errors: resultErrors }, usedFiles }
const descriptor = compiler.compileToDescriptor(filename, source)
const errors = combineErrors(descriptor.template, ...descriptor.styles)
if (errors.length > 0) {
return { errors }
}
if (options.flowtype) {
descriptor.script.code = flowRemoveTypes(descriptor.script.code, options.flowtype).toString()
}
const output = componentCompiler.assemble(compiler, source, result, {})
return { result: { contents: output.code }, usedFiles }
const output = componentCompiler.assemble(compiler, source, descriptor, {})
return { contents: output.code }
} catch (error) {
return {
result: {
errors: [
{
text: `Could not compile Vue single-file component: ${error}`,
detail: error
}
]
},
usedFiles
errors: [
{
text: `Could not compile Vue single-file component: ${error}`,
detail: error
}
]
}
}
}
Expand All @@ -113,7 +90,3 @@ function convertError (error) {
}
throw new Error(`Cannot convert Vue compiler error: ${error}`)
}

function editModule (name, fn) {
fn(require(name))
}

0 comments on commit 53864ab

Please sign in to comment.