From b4430522d1ba211b1f534ab79f79dba680f612c5 Mon Sep 17 00:00:00 2001 From: liuyue28 Date: Wed, 20 Jan 2021 18:03:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=AD=A3=E5=88=99=E7=A7=BB=E9=99=A4=E6=A8=A1=E5=9D=97=E5=BC=95?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/compilers/renderer-options.ts | 8 ++++++++ src/models/san-project.ts | 9 +++++++- src/parsers/remove-modules.ts | 23 +++++++++++++++++++++ test/stub/remove-modules.comp.ts | 12 +++++++++++ test/stub/remove-modules.d.ts | 2 ++ test/unit/models/san-project.spec.ts | 9 ++++++++ test/unit/parsers/remove-modules.spec.ts | 26 ++++++++++++++++++++++++ 7 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 src/parsers/remove-modules.ts create mode 100644 test/stub/remove-modules.comp.ts create mode 100644 test/stub/remove-modules.d.ts create mode 100644 test/unit/parsers/remove-modules.spec.ts diff --git a/src/compilers/renderer-options.ts b/src/compilers/renderer-options.ts index bd441cb8..a7fce299 100644 --- a/src/compilers/renderer-options.ts +++ b/src/compilers/renderer-options.ts @@ -2,4 +2,12 @@ export interface RenderOptions { functionName?: string ssrOnly?: boolean importHelpers?: string + /** + * 删除 ssr 不需要引入的模块,仅对 TypedSanSourceFile 有效 + */ + removeModules?: RegExp[] + /** + * 不同 target 实现的 CompilerOptions 可以继承并扩充字段 + */ + [key: string]: any; } diff --git a/src/models/san-project.ts b/src/models/san-project.ts index d19aed08..b99ff527 100644 --- a/src/models/san-project.ts +++ b/src/models/san-project.ts @@ -5,9 +5,11 @@ import { ComponentClassParser } from '../parsers/component-class-parser' import { TypeScriptSanParser } from '../parsers/typescript-san-parser' import { JavaScriptSanParser } from '../parsers/javascript-san-parser' import { SanFileParser } from '../parsers/san-file-parser' +import { removeModules } from '../parsers/remove-modules' import { TypedSanSourceFile, DynamicSanSourceFile, SanSourceFile } from '../models/san-source-file' import ToJSCompiler from '../target-js/index' import { CompileOptions } from '../target-js/compilers/compile-options' +import { RenderOptions } from '../compilers/renderer-options' import { Renderer } from './renderer' import { getDefaultTSConfigPath } from '../parsers/tsconfig' import { Compiler } from '../models/compiler' @@ -44,9 +46,14 @@ export class SanProject { public compileToSource ( input: CompileInput, target: string | CompilerClass = 'js', - options = {} + options: RenderOptions = {} ) { const sanSourceFile = this.parseSanSourceFile(input) + // 删除配置中指定的在 ssr 下无需引入的模块 + const { removeModules: modules } = options + if (modules && modules.length) { + removeModules(sanSourceFile, modules) + } const compiler = this.getOrCreateCompilerInstance(target) return compiler.compileToSource(sanSourceFile, options) } diff --git a/src/parsers/remove-modules.ts b/src/parsers/remove-modules.ts new file mode 100644 index 00000000..090e8d13 --- /dev/null +++ b/src/parsers/remove-modules.ts @@ -0,0 +1,23 @@ +import { SanSourceFile, isTypedSanSourceFile } from '../models/san-source-file' +import debugFactory from 'debug' + +const debug = debugFactory('san-ssr:remove-modules') + +/** + * 删除模块引用 + */ +export function removeModules (sanSourceFile: SanSourceFile, modules: RegExp[]) { + if (!isTypedSanSourceFile(sanSourceFile)) { + debug('TypedSanSourceFile is required') + return + } + const importDeclarations = sanSourceFile.tsSourceFile.getImportDeclarations() + debug('removing modules', importDeclarations) + importDeclarations.forEach(i => { + const specifierValue = i.getModuleSpecifierValue() + if (modules.some(re => re.test(specifierValue))) { + debug(`remove ${specifierValue}`) + i.remove() + } + }) +} diff --git a/test/stub/remove-modules.comp.ts b/test/stub/remove-modules.comp.ts new file mode 100644 index 00000000..b51be873 --- /dev/null +++ b/test/stub/remove-modules.comp.ts @@ -0,0 +1,12 @@ +/// +import foo from 'foo' +import bar from 'bar' +import { Component } from 'san' + +export default class RemoveModulesComp extends Component { + public static template = '
Remove foo
' + mounted () { + foo() + bar() + } +} diff --git a/test/stub/remove-modules.d.ts b/test/stub/remove-modules.d.ts new file mode 100644 index 00000000..aedcfa28 --- /dev/null +++ b/test/stub/remove-modules.d.ts @@ -0,0 +1,2 @@ +declare module 'foo' +declare module 'bar' diff --git a/test/unit/models/san-project.spec.ts b/test/unit/models/san-project.spec.ts index 689e33ed..eb341dcc 100644 --- a/test/unit/models/san-project.spec.ts +++ b/test/unit/models/san-project.spec.ts @@ -142,6 +142,15 @@ describe('SanProject', function () { expect(render).toBeInstanceOf(Function) expect(render({ name: 'Harttle' }, true)).toEqual('
name: Harttle
') }) + + it('should remove modules', function () { + const proj = new SanProject() + const code = proj.compileToSource(resolve(stubRoot, './remove-modules.comp.ts'), 'js', { + removeModules: [/^foo/] + }) + expect(code).not.toContain('require("foo")') + expect(code).toContain('require("bar")') + }) }) describe('.loadCompilerClassByTarget()', () => { let proj: SanProject diff --git a/test/unit/parsers/remove-modules.spec.ts b/test/unit/parsers/remove-modules.spec.ts new file mode 100644 index 00000000..7c72a8e9 --- /dev/null +++ b/test/unit/parsers/remove-modules.spec.ts @@ -0,0 +1,26 @@ +import { removeModules } from '../../../src/parsers/remove-modules' +import { TypedSanSourceFile } from '../../../src/models/san-source-file' +import { SanProject } from '../../../src/models/san-project' +import { resolve } from 'path' + +const testRoot = resolve(__dirname, '../../') +const stubRoot = resolve(testRoot, 'stub') + +const mockDebug = jest.fn() +jest.mock('debug', () => () => (str: string) => mockDebug(str)) + +describe('removeModules', () => { + it('should deal with TypedSanSourceFile only', () => { + const proj = new SanProject() + const sanSourceFile = proj.parseSanSourceFile(resolve(stubRoot, './a.comp.js')) + removeModules(sanSourceFile, []) + expect(mockDebug).toHaveBeenCalledWith('TypedSanSourceFile is required') + }) + + it('should remove modules', () => { + const proj = new SanProject(resolve(testRoot, '../tsconfig.json')) + const sanSourceFile = proj.parseSanSourceFile(resolve(stubRoot, './remove-modules.comp.ts')) as TypedSanSourceFile + removeModules(sanSourceFile, [/^foo/]) + expect(sanSourceFile.tsSourceFile.getImportDeclaration('foo')).toBeUndefined() + }) +})