mirror of https://github.com/vuejs/core.git
build: custom const enum processing
This commit is contained in:
parent
53e3533b75
commit
6213b73cd2
10
.eslintrc.js
10
.eslintrc.js
|
@ -17,13 +17,15 @@ module.exports = {
|
||||||
],
|
],
|
||||||
// most of the codebase are expected to be env agnostic
|
// most of the codebase are expected to be env agnostic
|
||||||
'no-restricted-globals': ['error', ...DOMGlobals, ...NodeGlobals],
|
'no-restricted-globals': ['error', ...DOMGlobals, ...NodeGlobals],
|
||||||
// since we target ES2015 for baseline support, we need to forbid object
|
|
||||||
// rest spread usage in destructure as it compiles into a verbose helper.
|
|
||||||
// TS now compiles assignment spread into Object.assign() calls so that
|
|
||||||
// is allowed.
|
|
||||||
'no-restricted-syntax': [
|
'no-restricted-syntax': [
|
||||||
'error',
|
'error',
|
||||||
|
// since we target ES2015 for baseline support, we need to forbid object
|
||||||
|
// rest spread usage in destructure as it compiles into a verbose helper.
|
||||||
'ObjectPattern > RestElement',
|
'ObjectPattern > RestElement',
|
||||||
|
// tsc compiles assignment spread into Object.assign() calls, but esbuild
|
||||||
|
// still generates verbose helpers, so spread assignment is also prohiboted
|
||||||
|
'ObjectExpression > SpreadElement',
|
||||||
'AwaitExpression'
|
'AwaitExpression'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
"node": ">=16.11.0"
|
"node": ">=16.11.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/parser": "^7.20.15",
|
||||||
"@babel/types": "^7.20.7",
|
"@babel/types": "^7.20.7",
|
||||||
"@esbuild-plugins/node-modules-polyfill": "^0.1.4",
|
"@esbuild-plugins/node-modules-polyfill": "^0.1.4",
|
||||||
"@microsoft/api-extractor": "~7.20.0",
|
"@microsoft/api-extractor": "~7.20.0",
|
||||||
|
@ -83,6 +84,7 @@
|
||||||
"jsdom": "^21.1.0",
|
"jsdom": "^21.1.0",
|
||||||
"lint-staged": "^10.2.10",
|
"lint-staged": "^10.2.10",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
|
"magic-string": "^0.27.0",
|
||||||
"marked": "^4.0.10",
|
"marked": "^4.0.10",
|
||||||
"minimist": "^1.2.0",
|
"minimist": "^1.2.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
|
|
|
@ -17,11 +17,22 @@ export function createSSRCompilerError(
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum SSRErrorCodes {
|
export const enum SSRErrorCodes {
|
||||||
X_SSR_UNSAFE_ATTR_NAME = DOMErrorCodes.__EXTEND_POINT__,
|
X_SSR_UNSAFE_ATTR_NAME = 62 /* DOMErrorCodes.__EXTEND_POINT__ */,
|
||||||
X_SSR_NO_TELEPORT_TARGET,
|
X_SSR_NO_TELEPORT_TARGET,
|
||||||
X_SSR_INVALID_AST_NODE
|
X_SSR_INVALID_AST_NODE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (__TEST__) {
|
||||||
|
// esbuild cannot infer const enum increments if first value is from another
|
||||||
|
// file, so we have to manually keep them in sync. this check ensures it
|
||||||
|
// errors out if there are collisions.
|
||||||
|
if (SSRErrorCodes.X_SSR_UNSAFE_ATTR_NAME < DOMErrorCodes.__EXTEND_POINT__) {
|
||||||
|
throw new Error(
|
||||||
|
'SSRErrorCodes need to be updated to match extension point from core DOMErrorCodes.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const SSRErrorMessages: { [code: number]: string } = {
|
export const SSRErrorMessages: { [code: number]: string } = {
|
||||||
[SSRErrorCodes.X_SSR_UNSAFE_ATTR_NAME]: `Unsafe attribute name for SSR.`,
|
[SSRErrorCodes.X_SSR_UNSAFE_ATTR_NAME]: `Unsafe attribute name for SSR.`,
|
||||||
[SSRErrorCodes.X_SSR_NO_TELEPORT_TARGET]: `Missing the 'to' prop on teleport element.`,
|
[SSRErrorCodes.X_SSR_NO_TELEPORT_TARGET]: `Missing the 'to' prop on teleport element.`,
|
||||||
|
|
|
@ -248,10 +248,14 @@ export function trackEffects(
|
||||||
dep.add(activeEffect!)
|
dep.add(activeEffect!)
|
||||||
activeEffect!.deps.push(dep)
|
activeEffect!.deps.push(dep)
|
||||||
if (__DEV__ && activeEffect!.onTrack) {
|
if (__DEV__ && activeEffect!.onTrack) {
|
||||||
activeEffect!.onTrack({
|
activeEffect!.onTrack(
|
||||||
effect: activeEffect!,
|
extend(
|
||||||
...debuggerEventExtraInfo!
|
{
|
||||||
})
|
effect: activeEffect!
|
||||||
|
},
|
||||||
|
debuggerEventExtraInfo!
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ export {
|
||||||
shallowReadonly,
|
shallowReadonly,
|
||||||
markRaw,
|
markRaw,
|
||||||
toRaw,
|
toRaw,
|
||||||
ReactiveFlags,
|
ReactiveFlags /* @remove */,
|
||||||
type Raw,
|
type Raw,
|
||||||
type DeepReadonly,
|
type DeepReadonly,
|
||||||
type ShallowReactive,
|
type ShallowReactive,
|
||||||
|
@ -66,4 +66,7 @@ export {
|
||||||
getCurrentScope,
|
getCurrentScope,
|
||||||
onScopeDispose
|
onScopeDispose
|
||||||
} from './effectScope'
|
} from './effectScope'
|
||||||
export { TrackOpTypes, TriggerOpTypes } from './operations'
|
export {
|
||||||
|
TrackOpTypes /* @remove */,
|
||||||
|
TriggerOpTypes /* @remove */
|
||||||
|
} from './operations'
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { warn } from './warning'
|
||||||
import { createVNode, cloneVNode, VNode } from './vnode'
|
import { createVNode, cloneVNode, VNode } from './vnode'
|
||||||
import { RootHydrateFunction } from './hydration'
|
import { RootHydrateFunction } from './hydration'
|
||||||
import { devtoolsInitApp, devtoolsUnmountApp } from './devtools'
|
import { devtoolsInitApp, devtoolsUnmountApp } from './devtools'
|
||||||
import { isFunction, NO, isObject } from '@vue/shared'
|
import { isFunction, NO, isObject, extend } from '@vue/shared'
|
||||||
import { version } from '.'
|
import { version } from '.'
|
||||||
import { installAppCompatProperties } from './compat/global'
|
import { installAppCompatProperties } from './compat/global'
|
||||||
import { NormalizedPropsOptions } from './componentProps'
|
import { NormalizedPropsOptions } from './componentProps'
|
||||||
|
@ -193,7 +193,7 @@ export function createAppAPI<HostElement>(
|
||||||
): CreateAppFunction<HostElement> {
|
): CreateAppFunction<HostElement> {
|
||||||
return function createApp(rootComponent, rootProps = null) {
|
return function createApp(rootComponent, rootProps = null) {
|
||||||
if (!isFunction(rootComponent)) {
|
if (!isFunction(rootComponent)) {
|
||||||
rootComponent = { ...rootComponent }
|
rootComponent = extend({}, rootComponent)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rootProps != null && !isObject(rootProps)) {
|
if (rootProps != null && !isObject(rootProps)) {
|
||||||
|
|
|
@ -22,7 +22,8 @@ import {
|
||||||
remove,
|
remove,
|
||||||
isMap,
|
isMap,
|
||||||
isSet,
|
isSet,
|
||||||
isPlainObject
|
isPlainObject,
|
||||||
|
extend
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
currentInstance,
|
currentInstance,
|
||||||
|
@ -94,7 +95,7 @@ export function watchPostEffect(
|
||||||
return doWatch(
|
return doWatch(
|
||||||
effect,
|
effect,
|
||||||
null,
|
null,
|
||||||
__DEV__ ? { ...options, flush: 'post' } : { flush: 'post' }
|
__DEV__ ? extend({}, options as any, { flush: 'post' }) : { flush: 'post' }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +106,7 @@ export function watchSyncEffect(
|
||||||
return doWatch(
|
return doWatch(
|
||||||
effect,
|
effect,
|
||||||
null,
|
null,
|
||||||
__DEV__ ? { ...options, flush: 'sync' } : { flush: 'sync' }
|
__DEV__ ? extend({}, options as any, { flush: 'sync' }) : { flush: 'sync' }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -522,7 +522,7 @@ export function normalizePropsOptions(
|
||||||
if (validatePropName(normalizedKey)) {
|
if (validatePropName(normalizedKey)) {
|
||||||
const opt = raw[key]
|
const opt = raw[key]
|
||||||
const prop: NormalizedProp = (normalized[normalizedKey] =
|
const prop: NormalizedProp = (normalized[normalizedKey] =
|
||||||
isArray(opt) || isFunction(opt) ? { type: opt } : { ...opt })
|
isArray(opt) || isFunction(opt) ? { type: opt } : extend({}, opt))
|
||||||
if (prop) {
|
if (prop) {
|
||||||
const booleanIndex = getTypeIndex(Boolean, prop.type)
|
const booleanIndex = getTypeIndex(Boolean, prop.type)
|
||||||
const stringIndex = getTypeIndex(String, prop.type)
|
const stringIndex = getTypeIndex(String, prop.type)
|
||||||
|
|
|
@ -4,6 +4,7 @@ importers:
|
||||||
|
|
||||||
.:
|
.:
|
||||||
specifiers:
|
specifiers:
|
||||||
|
'@babel/parser': ^7.20.15
|
||||||
'@babel/types': ^7.20.7
|
'@babel/types': ^7.20.7
|
||||||
'@esbuild-plugins/node-modules-polyfill': ^0.1.4
|
'@esbuild-plugins/node-modules-polyfill': ^0.1.4
|
||||||
'@microsoft/api-extractor': ~7.20.0
|
'@microsoft/api-extractor': ~7.20.0
|
||||||
|
@ -33,6 +34,7 @@ importers:
|
||||||
jsdom: ^21.1.0
|
jsdom: ^21.1.0
|
||||||
lint-staged: ^10.2.10
|
lint-staged: ^10.2.10
|
||||||
lodash: ^4.17.15
|
lodash: ^4.17.15
|
||||||
|
magic-string: ^0.27.0
|
||||||
marked: ^4.0.10
|
marked: ^4.0.10
|
||||||
minimist: ^1.2.0
|
minimist: ^1.2.0
|
||||||
npm-run-all: ^4.1.5
|
npm-run-all: ^4.1.5
|
||||||
|
@ -50,11 +52,12 @@ importers:
|
||||||
terser: ^5.15.1
|
terser: ^5.15.1
|
||||||
todomvc-app-css: ^2.3.0
|
todomvc-app-css: ^2.3.0
|
||||||
tslib: ^2.4.0
|
tslib: ^2.4.0
|
||||||
typescript: ^4.8.0
|
typescript: ^4.9.0
|
||||||
vite: ^4.0.4
|
vite: ^4.0.4
|
||||||
vitest: ^0.28.2
|
vitest: ^0.28.2
|
||||||
vue: workspace:*
|
vue: workspace:*
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@babel/parser': 7.20.15
|
||||||
'@babel/types': 7.20.7
|
'@babel/types': 7.20.7
|
||||||
'@esbuild-plugins/node-modules-polyfill': 0.1.4_esbuild@0.17.5
|
'@esbuild-plugins/node-modules-polyfill': 0.1.4_esbuild@0.17.5
|
||||||
'@microsoft/api-extractor': 7.20.1
|
'@microsoft/api-extractor': 7.20.1
|
||||||
|
@ -84,6 +87,7 @@ importers:
|
||||||
jsdom: 21.1.0
|
jsdom: 21.1.0
|
||||||
lint-staged: 10.5.4
|
lint-staged: 10.5.4
|
||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
|
magic-string: 0.27.0
|
||||||
marked: 4.2.12
|
marked: 4.2.12
|
||||||
minimist: 1.2.7
|
minimist: 1.2.7
|
||||||
npm-run-all: 4.1.5
|
npm-run-all: 4.1.5
|
||||||
|
|
|
@ -12,6 +12,8 @@ import terser from '@rollup/plugin-terser'
|
||||||
import esbuild from 'rollup-plugin-esbuild'
|
import esbuild from 'rollup-plugin-esbuild'
|
||||||
import alias from '@rollup/plugin-alias'
|
import alias from '@rollup/plugin-alias'
|
||||||
import { entries } from './scripts/aliases.mjs'
|
import { entries } from './scripts/aliases.mjs'
|
||||||
|
import { constEnum } from './scripts/const-enum.mjs'
|
||||||
|
import { writeFileSync } from 'node:fs'
|
||||||
|
|
||||||
if (!process.env.TARGET) {
|
if (!process.env.TARGET) {
|
||||||
throw new Error('TARGET package must be specified via --environment flag.')
|
throw new Error('TARGET package must be specified via --environment flag.')
|
||||||
|
@ -31,6 +33,8 @@ const pkg = require(resolve(`package.json`))
|
||||||
const packageOptions = pkg.buildOptions || {}
|
const packageOptions = pkg.buildOptions || {}
|
||||||
const name = packageOptions.filename || path.basename(packageDir)
|
const name = packageOptions.filename || path.basename(packageDir)
|
||||||
|
|
||||||
|
const [enumPlugin, enumDefines] = await constEnum()
|
||||||
|
|
||||||
const outputConfigs = {
|
const outputConfigs = {
|
||||||
'esm-bundler': {
|
'esm-bundler': {
|
||||||
file: resolve(`dist/${name}.esm-bundler.js`),
|
file: resolve(`dist/${name}.esm-bundler.js`),
|
||||||
|
@ -175,7 +179,7 @@ function createConfig(format, output, plugins = []) {
|
||||||
// esbuild define is a bit strict and only allows literal json or identifiers
|
// esbuild define is a bit strict and only allows literal json or identifiers
|
||||||
// so we still need replace plugin in some cases
|
// so we still need replace plugin in some cases
|
||||||
function resolveReplace() {
|
function resolveReplace() {
|
||||||
const replacements = {}
|
const replacements = { ...enumDefines }
|
||||||
|
|
||||||
if (isProductionBuild && isBrowserBuild) {
|
if (isProductionBuild && isBrowserBuild) {
|
||||||
Object.assign(replacements, {
|
Object.assign(replacements, {
|
||||||
|
@ -282,6 +286,7 @@ function createConfig(format, output, plugins = []) {
|
||||||
alias({
|
alias({
|
||||||
entries
|
entries
|
||||||
}),
|
}),
|
||||||
|
enumPlugin,
|
||||||
...resolveReplace(),
|
...resolveReplace(),
|
||||||
esbuild({
|
esbuild({
|
||||||
tsconfig: path.resolve(__dirname, 'tsconfig.json'),
|
tsconfig: path.resolve(__dirname, 'tsconfig.json'),
|
||||||
|
|
|
@ -0,0 +1,192 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We use rollup-plugin-esbuild for faster builds, but esbuild in insolation
|
||||||
|
* mode compiles const enums into runtime enums, bloating bundle size.
|
||||||
|
*
|
||||||
|
* Here we pre-process all the const enums in the project and turn them into
|
||||||
|
* global replacements, and remove the original declarations and re-exports.
|
||||||
|
*
|
||||||
|
* This erases the const enums before the esbuild transform so that we can
|
||||||
|
* leverage esbuild's speed while retaining the DX and bundle size benefits
|
||||||
|
* of const enums.
|
||||||
|
*
|
||||||
|
* This file is expected to be executed with project root as cwd.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import execa from 'execa'
|
||||||
|
import { readFileSync } from 'node:fs'
|
||||||
|
import { parse } from '@babel/parser'
|
||||||
|
import path from 'node:path'
|
||||||
|
import MagicString from 'magic-string'
|
||||||
|
|
||||||
|
function evaluate(exp) {
|
||||||
|
return new Function(`return ${exp}`)()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Promise<[import('rollup').Plugin, Record<string, string>]>}
|
||||||
|
*/
|
||||||
|
export async function constEnum() {
|
||||||
|
/**
|
||||||
|
* @type {{ ranges: Record<string, [number, number][]>, defines: Record<string, string> }}
|
||||||
|
*/
|
||||||
|
const enumData = {
|
||||||
|
ranges: {},
|
||||||
|
defines: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const knowEnums = new Set()
|
||||||
|
|
||||||
|
// 1. grep for files with exported const enum
|
||||||
|
const { stdout } = await execa('git', ['grep', `export const enum`])
|
||||||
|
const files = [...new Set(stdout.split('\n').map(line => line.split(':')[0]))]
|
||||||
|
|
||||||
|
// 2. parse matched files to collect enum info
|
||||||
|
for (const relativeFile of files) {
|
||||||
|
const file = path.resolve(process.cwd(), relativeFile)
|
||||||
|
const content = readFileSync(file, 'utf-8')
|
||||||
|
const ast = parse(content, {
|
||||||
|
plugins: ['typescript'],
|
||||||
|
sourceType: 'module'
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const node of ast.program.body) {
|
||||||
|
if (
|
||||||
|
node.type === 'ExportNamedDeclaration' &&
|
||||||
|
node.declaration &&
|
||||||
|
node.declaration.type === 'TSEnumDeclaration'
|
||||||
|
) {
|
||||||
|
if (file in enumData.ranges) {
|
||||||
|
// @ts-ignore
|
||||||
|
enumData.ranges[file].push([node.start, node.end])
|
||||||
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
|
enumData.ranges[file] = [[node.start, node.end]]
|
||||||
|
}
|
||||||
|
|
||||||
|
const decl = node.declaration
|
||||||
|
let lastInitialized
|
||||||
|
for (let i = 0; i < decl.members.length; i++) {
|
||||||
|
const e = decl.members[i]
|
||||||
|
const id = decl.id.name
|
||||||
|
knowEnums.add(id)
|
||||||
|
const key = e.id.type === 'Identifier' ? e.id.name : e.id.value
|
||||||
|
const fullKey = `${id}.${key}`
|
||||||
|
const init = e.initializer
|
||||||
|
if (init) {
|
||||||
|
let value
|
||||||
|
if (
|
||||||
|
init.type === 'StringLiteral' ||
|
||||||
|
init.type === 'NumericLiteral'
|
||||||
|
) {
|
||||||
|
value = init.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// e.g. 1 << 2
|
||||||
|
if (init.type === 'BinaryExpression') {
|
||||||
|
// @ts-ignore assume all operands are literals
|
||||||
|
const exp = `${init.left.value}${init.operator}${init.right.value}`
|
||||||
|
value = evaluate(exp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init.type === 'UnaryExpression') {
|
||||||
|
// @ts-ignore assume all operands are literals
|
||||||
|
const exp = `${init.operator}${init.argument.value}`
|
||||||
|
value = evaluate(exp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === undefined) {
|
||||||
|
throw new Error(
|
||||||
|
`unhandled initializer type ${init.type} for ${fullKey} in ${file}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
enumData.defines[fullKey] = JSON.stringify(value)
|
||||||
|
lastInitialized = value
|
||||||
|
} else {
|
||||||
|
if (lastInitialized === undefined) {
|
||||||
|
// first initialized
|
||||||
|
enumData.defines[fullKey] = `0`
|
||||||
|
lastInitialized = 0
|
||||||
|
} else if (typeof lastInitialized === 'number') {
|
||||||
|
enumData.defines[fullKey] = String(++lastInitialized)
|
||||||
|
} else {
|
||||||
|
// should not happen
|
||||||
|
throw new Error(`wrong enum initialization sequence in ${file}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// construct a regex for matching re-exports of known const enums
|
||||||
|
const reExportsRE = new RegExp(
|
||||||
|
`export {[^}]*?\\b(${[...knowEnums].join('|')})\\b[^]*?}`
|
||||||
|
)
|
||||||
|
|
||||||
|
// 3. during transform:
|
||||||
|
// 3.1 files w/ const enum declaration: remove delcaration
|
||||||
|
// 3.2 files using const enum: inject into esbuild define
|
||||||
|
/**
|
||||||
|
* @type {import('rollup').Plugin}
|
||||||
|
*/
|
||||||
|
const plugin = {
|
||||||
|
name: 'remove-const-enum',
|
||||||
|
transform(code, id) {
|
||||||
|
let s
|
||||||
|
|
||||||
|
if (id in enumData.ranges) {
|
||||||
|
s = s || new MagicString(code)
|
||||||
|
for (const [start, end] of enumData.ranges[id]) {
|
||||||
|
s.remove(start, end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for const enum re-exports that must be removed
|
||||||
|
if (reExportsRE.test(code)) {
|
||||||
|
s = s || new MagicString(code)
|
||||||
|
const ast = parse(code, {
|
||||||
|
plugins: ['typescript'],
|
||||||
|
sourceType: 'module'
|
||||||
|
})
|
||||||
|
for (const node of ast.program.body) {
|
||||||
|
if (
|
||||||
|
node.type === 'ExportNamedDeclaration' &&
|
||||||
|
node.exportKind !== 'type' &&
|
||||||
|
node.source
|
||||||
|
) {
|
||||||
|
for (let i = 0; i < node.specifiers.length; i++) {
|
||||||
|
const spec = node.specifiers[i]
|
||||||
|
if (
|
||||||
|
spec.type === 'ExportSpecifier' &&
|
||||||
|
spec.exportKind !== 'type' &&
|
||||||
|
knowEnums.has(spec.local.name)
|
||||||
|
) {
|
||||||
|
if (i === 0) {
|
||||||
|
// first
|
||||||
|
const next = node.specifiers[i + 1]
|
||||||
|
// @ts-ignore
|
||||||
|
s.remove(spec.start, next ? next.start : spec.end)
|
||||||
|
} else {
|
||||||
|
// locate the end of prev
|
||||||
|
// @ts-ignore
|
||||||
|
s.remove(node.specifiers[i - 1].end, spec.end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s) {
|
||||||
|
return {
|
||||||
|
code: s.toString(),
|
||||||
|
map: s.generateMap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [plugin, enumData.defines]
|
||||||
|
}
|
Loading…
Reference in New Issue