mirror of https://github.com/alibaba/ice.git
				
				
				
			Refactor: use swc plugin to remove code (#6144)
* refactor: use swc plugin to remove code * chore: update version
This commit is contained in:
		
							parent
							
								
									ce94e05489
								
							
						
					
					
						commit
						5dd3c86ed7
					
				|  | @ -0,0 +1,6 @@ | ||||||
|  | --- | ||||||
|  | '@ice/webpack-config': patch | ||||||
|  | '@ice/app': patch | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | refactor: use swc plugin for remove code | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | --- | ||||||
|  | '@ice/bundles': patch | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | fix: update version of @ice/swc-plugin-keep-export(0.1.4-2) | ||||||
|  | @ -17,7 +17,7 @@ | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@swc/core": "1.3.19", |     "@swc/core": "1.3.19", | ||||||
|     "@ice/swc-plugin-remove-export": "0.1.2", |     "@ice/swc-plugin-remove-export": "0.1.2", | ||||||
|     "@ice/swc-plugin-keep-export": "0.1.4", |     "@ice/swc-plugin-keep-export": "0.1.4-2", | ||||||
|     "@ice/swc-plugin-node-transform": "0.1.0-5", |     "@ice/swc-plugin-node-transform": "0.1.0-5", | ||||||
|     "ansi-html-community": "^0.0.8", |     "ansi-html-community": "^0.0.8", | ||||||
|     "html-entities": "^2.3.2", |     "html-entities": "^2.3.2", | ||||||
|  |  | ||||||
|  | @ -36,10 +36,6 @@ | ||||||
|   "bugs": "https://github.com/alibaba/ice/issues", |   "bugs": "https://github.com/alibaba/ice/issues", | ||||||
|   "homepage": "https://v3.ice.work", |   "homepage": "https://v3.ice.work", | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@babel/generator": "7.18.10", |  | ||||||
|     "@babel/parser": "7.18.10", |  | ||||||
|     "@babel/traverse": "7.18.10", |  | ||||||
|     "@babel/types": "7.18.10", |  | ||||||
|     "@ice/bundles": "0.1.8", |     "@ice/bundles": "0.1.8", | ||||||
|     "@ice/route-manifest": "1.1.1", |     "@ice/route-manifest": "1.1.1", | ||||||
|     "@ice/runtime": "^1.1.5", |     "@ice/runtime": "^1.1.5", | ||||||
|  | @ -56,7 +52,6 @@ | ||||||
|     "dotenv": "^16.0.0", |     "dotenv": "^16.0.0", | ||||||
|     "dotenv-expand": "^8.0.3", |     "dotenv-expand": "^8.0.3", | ||||||
|     "ejs": "^3.1.6", |     "ejs": "^3.1.6", | ||||||
|     "estree-walker": "^3.0.2", |  | ||||||
|     "fast-glob": "^3.2.11", |     "fast-glob": "^3.2.11", | ||||||
|     "find-up": "^5.0.0", |     "find-up": "^5.0.0", | ||||||
|     "fs-extra": "^10.0.0", |     "fs-extra": "^10.0.0", | ||||||
|  |  | ||||||
|  | @ -1,55 +0,0 @@ | ||||||
| import * as fs from 'fs'; |  | ||||||
| import type { Plugin } from 'esbuild'; |  | ||||||
| import { parse, type ParserOptions } from '@babel/parser'; |  | ||||||
| import babelTraverse from '@babel/traverse'; |  | ||||||
| import babelGenerate from '@babel/generator'; |  | ||||||
| import removeTopLevelCode from '../utils/babelPluginRemoveCode.js'; |  | ||||||
| import formatPath from '../utils/formatPath.js'; |  | ||||||
| import { logger } from '../utils/logger.js'; |  | ||||||
| 
 |  | ||||||
| // @ts-ignore @babel/traverse is not a valid export in esm
 |  | ||||||
| const traverse = babelTraverse.default || babelTraverse; |  | ||||||
| // @ts-ignore @babel/generate is not a valid export in esm
 |  | ||||||
| const generate = babelGenerate.default || babelGenerate; |  | ||||||
| 
 |  | ||||||
| const removeCodePlugin = (keepExports: string[], transformInclude: (id: string) => boolean): Plugin => { |  | ||||||
|   const parserOptions: ParserOptions = { |  | ||||||
|     sourceType: 'module', |  | ||||||
|     plugins: [ |  | ||||||
|       'jsx', |  | ||||||
|       'importMeta', |  | ||||||
|       'topLevelAwait', |  | ||||||
|       'classProperties', |  | ||||||
|       'classPrivateMethods', |  | ||||||
|     ], |  | ||||||
|   }; |  | ||||||
|   return { |  | ||||||
|     name: 'esbuild-remove-top-level-code', |  | ||||||
|     setup(build) { |  | ||||||
|       build.onLoad({ filter: /\.(js|jsx|ts|tsx)$/ }, async ({ path: id }) => { |  | ||||||
|         if (!transformInclude(formatPath(id))) { |  | ||||||
|           return; |  | ||||||
|         } |  | ||||||
|         const source = await fs.promises.readFile(id, 'utf-8'); |  | ||||||
|         let isTS = false; |  | ||||||
|         if (id.match(/\.(ts|tsx)$/)) { |  | ||||||
|           isTS = true; |  | ||||||
|           parserOptions.plugins.push('typescript', 'decorators-legacy'); |  | ||||||
|         } |  | ||||||
|         try { |  | ||||||
|           const ast = parse(source, parserOptions); |  | ||||||
|           traverse(ast, removeTopLevelCode(keepExports)); |  | ||||||
|           const contents = generate(ast).code; |  | ||||||
|           return { |  | ||||||
|             contents, |  | ||||||
|             loader: isTS ? 'tsx' : 'jsx', |  | ||||||
|           }; |  | ||||||
|         } catch (error) { |  | ||||||
|           logger.debug('Remove top level code error.', `\nFile id: ${id}`, `\n${error.stack}`); |  | ||||||
|         } |  | ||||||
|       }); |  | ||||||
|     }, |  | ||||||
|   }; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export default removeCodePlugin; |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| import * as path from 'path'; | import * as path from 'path'; | ||||||
| import fs from 'fs-extra'; | import fs from 'fs-extra'; | ||||||
| import type { ServerCompiler } from '../types/plugin.js'; | import type { ServerCompiler } from '../types/plugin.js'; | ||||||
| import removeTopLevelCode from '../esbuild/removeTopLevelCode.js'; |  | ||||||
| import { getCache, setCache } from '../utils/persistentCache.js'; | import { getCache, setCache } from '../utils/persistentCache.js'; | ||||||
| import { getFileHash } from '../utils/hash.js'; | import { getFileHash } from '../utils/hash.js'; | ||||||
| import dynamicImport from '../utils/dynamicImport.js'; | import dynamicImport from '../utils/dynamicImport.js'; | ||||||
|  | @ -15,7 +14,7 @@ type GetOutfile = (entry: string, exportNames: string[]) => string; | ||||||
| interface CompileConfig { | interface CompileConfig { | ||||||
|   entry: string; |   entry: string; | ||||||
|   rootDir: string; |   rootDir: string; | ||||||
|   transformInclude: (id: string) => boolean; |   transformInclude?: (id: string) => boolean; | ||||||
|   needRecompile?: (entry: string, options: string[]) => Promise<boolean | string>; |   needRecompile?: (entry: string, options: string[]) => Promise<boolean | string>; | ||||||
|   getOutfile?: GetOutfile; |   getOutfile?: GetOutfile; | ||||||
| } | } | ||||||
|  | @ -48,13 +47,19 @@ class Config { | ||||||
|         format: 'esm', |         format: 'esm', | ||||||
|         outfile, |         outfile, | ||||||
|         plugins: [ |         plugins: [ | ||||||
|           removeTopLevelCode(keepExports, transformInclude), |  | ||||||
|           // External node builtin modules, such as `fs`, it will be imported by weex document.
 |           // External node builtin modules, such as `fs`, it will be imported by weex document.
 | ||||||
|           externalBuiltinPlugin(), |           externalBuiltinPlugin(), | ||||||
|         ], |         ].filter(Boolean), | ||||||
|         sourcemap: false, |         sourcemap: false, | ||||||
|         logLevel: 'silent', // The main server compiler process will log it.
 |         logLevel: 'silent', // The main server compiler process will log it.
 | ||||||
|       }, {}); |       }, { | ||||||
|  |         swc: { | ||||||
|  |           keepExports: { | ||||||
|  |             value: keepExports, | ||||||
|  |             include: transformInclude, | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |       }); | ||||||
|       if (!error) { |       if (!error) { | ||||||
|         this.status = 'RESOLVED'; |         this.status = 'RESOLVED'; | ||||||
|         return outfile; |         return outfile; | ||||||
|  | @ -197,8 +202,6 @@ export const getRouteExportConfig = (rootDir: string) => { | ||||||
|     entry: routeConfigFile, |     entry: routeConfigFile, | ||||||
|     rootDir, |     rootDir, | ||||||
|     getOutfile: getRouteConfigOutfile, |     getOutfile: getRouteConfigOutfile, | ||||||
|     // Only remove top level code for route component file.
 |  | ||||||
|     transformInclude: (id) => id.includes('src/pages'), |  | ||||||
|     needRecompile: async (entry) => { |     needRecompile: async (entry) => { | ||||||
|       let cached = false; |       let cached = false; | ||||||
|       try { |       try { | ||||||
|  | @ -218,8 +221,6 @@ export const getRouteExportConfig = (rootDir: string) => { | ||||||
|     entry: loadersConfigFile, |     entry: loadersConfigFile, | ||||||
|     rootDir, |     rootDir, | ||||||
|     getOutfile: getdataLoadersConfigOutfile, |     getOutfile: getdataLoadersConfigOutfile, | ||||||
|     // Only remove top level code for route component file.
 |  | ||||||
|     transformInclude: (id) => id.includes('src/pages'), |  | ||||||
|     needRecompile: async (entry) => { |     needRecompile: async (entry) => { | ||||||
|       let cached = false; |       let cached = false; | ||||||
|       const cachedKey = `loader_config_file_${process.env.__ICE_VERSION__}`; |       const cachedKey = `loader_config_file_${process.env.__ICE_VERSION__}`; | ||||||
|  |  | ||||||
|  | @ -1,128 +0,0 @@ | ||||||
| import type { NodePath } from '@babel/traverse'; |  | ||||||
| import * as t from '@babel/types'; |  | ||||||
| 
 |  | ||||||
| const removeUnreferencedCode = (nodePath: NodePath<t.Program>) => { |  | ||||||
|   let hasRemoved = false; |  | ||||||
| 
 |  | ||||||
|   // Update bindings removed in enter hooks.
 |  | ||||||
|   nodePath.scope.crawl(); |  | ||||||
|   for (const [, binding] of Object.entries(nodePath.scope.bindings)) { |  | ||||||
|     if (!binding.referenced && binding.path.node) { |  | ||||||
|       const nodeType = binding.path.node.type; |  | ||||||
|       if (['VariableDeclarator', 'ImportSpecifier', 'FunctionDeclaration'].includes(nodeType)) { |  | ||||||
|         if (nodeType === 'ImportSpecifier' && (binding.path.parentPath.node as t.ImportDeclaration)?.specifiers.length === 1) { |  | ||||||
|           binding.path.parentPath.remove(); |  | ||||||
|         } else if (nodeType === 'VariableDeclarator') { |  | ||||||
|           if (binding.identifier === binding.path.node.id) { |  | ||||||
|             binding.path.remove(); |  | ||||||
|           } else { |  | ||||||
|             if (binding.path.node.id.type === 'ArrayPattern') { |  | ||||||
|               binding.path.node.id.elements = binding.path.node.id.elements.filter((element) => |  | ||||||
|                 (element !== binding.identifier && (element as t.RestElement)?.argument !== binding.identifier)); |  | ||||||
|               if (binding.path.node.id.elements.length === 0) { |  | ||||||
|                 binding.path.remove(); |  | ||||||
|               } |  | ||||||
|             } else if (binding.path.node.id.type === 'ObjectPattern') { |  | ||||||
|               binding.path.node.id.properties = binding.path.node.id.properties.filter((property) => { |  | ||||||
|                 const { value, key } = property as t.ObjectProperty; |  | ||||||
|                 const argument = (property as t.RestElement)?.argument; |  | ||||||
|                 return value !== binding.identifier && argument !== binding.identifier && |  | ||||||
|                   (key?.type !== 'Identifier' || (key as t.Identifier)?.name !== binding.identifier.name); |  | ||||||
|               }); |  | ||||||
|               if (binding.path.node.id.properties.length === 0) { |  | ||||||
|                 binding.path.remove(); |  | ||||||
|               } |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|         } else { |  | ||||||
|           binding.path.remove(); |  | ||||||
|         } |  | ||||||
|         hasRemoved = true; |  | ||||||
|       } else if (['ImportDefaultSpecifier'].includes(nodeType)) { |  | ||||||
|         binding.path.parentPath.remove(); |  | ||||||
|         hasRemoved = true; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   if (hasRemoved) { |  | ||||||
|     // Remove code until there is no more to removed.
 |  | ||||||
|     removeUnreferencedCode(nodePath); |  | ||||||
|   } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const keepExportCode = (identifier: t.Identifier, keepExports: string[]) => { |  | ||||||
|   return keepExports.some((exportString) => { |  | ||||||
|     return t.isIdentifier(identifier, { name: exportString }); |  | ||||||
|   }); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const removeTopLevelCode = (keepExports: string[] = []) => { |  | ||||||
|   return { |  | ||||||
|     ExportNamedDeclaration: { |  | ||||||
|       enter(nodePath: NodePath<t.ExportNamedDeclaration>) { |  | ||||||
|         const { node } = nodePath; |  | ||||||
|         // Exp: export function pageConfig() {}
 |  | ||||||
|         const isFunctionExport = t.isFunctionDeclaration(node.declaration) && |  | ||||||
|           keepExportCode(node.declaration.id, keepExports); |  | ||||||
|         // Exp: export const pageConfig = () => {}
 |  | ||||||
|         const isVariableExport = t.isVariableDeclaration(node.declaration) && |  | ||||||
|           keepExportCode(node.declaration.declarations![0]?.id as t.Identifier, keepExports); |  | ||||||
|         // Exp: export { pageConfig };
 |  | ||||||
|         if (node.specifiers && node.specifiers.length > 0) { |  | ||||||
|           nodePath.traverse({ |  | ||||||
|             ExportSpecifier(nodePath: NodePath<t.ExportSpecifier>) { |  | ||||||
|               if (!keepExportCode(nodePath.node.exported as t.Identifier, keepExports)) { |  | ||||||
|                 nodePath.remove(); |  | ||||||
|               } |  | ||||||
|             }, |  | ||||||
|           }); |  | ||||||
|           node.specifiers = node.specifiers.filter(specifier => |  | ||||||
|             keepExportCode(specifier.exported as t.Identifier, keepExports)); |  | ||||||
|         } else if (!isFunctionExport && !isVariableExport) { |  | ||||||
|           // Remove named export expect defined in keepExports.
 |  | ||||||
|           nodePath.remove(); |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|     }, |  | ||||||
|     ExportDefaultDeclaration: { |  | ||||||
|       enter(nodePath: NodePath<t.ExportDefaultDeclaration>) { |  | ||||||
|         // Remove default export declaration.
 |  | ||||||
|         if (!keepExports.includes('default')) { |  | ||||||
|           nodePath.remove(); |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|     }, |  | ||||||
|     ExpressionStatement: { |  | ||||||
|       enter(nodePath: NodePath<t.ExpressionStatement>) { |  | ||||||
|         // Remove top level call expression.
 |  | ||||||
|         if (nodePath.parentPath.isProgram()) { |  | ||||||
|           if (t.isCallExpression(nodePath.node.expression) || t.isAssignmentExpression(nodePath.node.expression)) { |  | ||||||
|             nodePath.remove(); |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|     }, |  | ||||||
|     ImportDeclaration: { |  | ||||||
|       enter(nodePath: NodePath<t.ImportDeclaration>) { |  | ||||||
|         // Remove import statement without specifiers.
 |  | ||||||
|         if (nodePath.node.specifiers.length === 0) { |  | ||||||
|           nodePath.remove(); |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|     }, |  | ||||||
|     'IfStatement|TryStatement|WhileStatement|DoWhileStatement': { |  | ||||||
|       // Remove statement even if it's may cause variable changed.
 |  | ||||||
|       enter(nodePath: NodePath<t.IfStatement | t.TryStatement | t.WhileStatement>) { |  | ||||||
|         // TODO: check expression statement if it is changed top level variable referenced by pageConfig
 |  | ||||||
|         nodePath.remove(); |  | ||||||
|       }, |  | ||||||
|     }, |  | ||||||
|     Program: { |  | ||||||
|       exit(nodePath: NodePath<t.Program>) { |  | ||||||
|         removeUnreferencedCode(nodePath); |  | ||||||
|       }, |  | ||||||
|     }, |  | ||||||
|   }; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export default removeTopLevelCode; |  | ||||||
|  | @ -1,2 +0,0 @@ | ||||||
| const a = 1; |  | ||||||
| export default a; |  | ||||||
|  | @ -1,6 +0,0 @@ | ||||||
| const pageConfig = () => {}; |  | ||||||
| const getData = () => {}; |  | ||||||
| export { |  | ||||||
|   pageConfig, |  | ||||||
|   getData, |  | ||||||
| }; |  | ||||||
|  | @ -1,2 +0,0 @@ | ||||||
| export const getData = () => {}; |  | ||||||
| export const pageConfig = () => {}; |  | ||||||
|  | @ -1,2 +0,0 @@ | ||||||
| const a = {}; |  | ||||||
| a.test = 1; |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| export default function Bar() {} |  | ||||||
| export function pageConfig() {} |  | ||||||
| export function getData() {} |  | ||||||
|  | @ -1,4 +0,0 @@ | ||||||
| let a = 1; |  | ||||||
| if (true) { |  | ||||||
|   a = 2; |  | ||||||
| } |  | ||||||
|  | @ -1,5 +0,0 @@ | ||||||
| function a() {} |  | ||||||
| a(); |  | ||||||
| console.log('test', window.a); |  | ||||||
| const b = []; |  | ||||||
| b.map(() => {}); |  | ||||||
|  | @ -1,8 +0,0 @@ | ||||||
| import { a, b } from 'test'; |  | ||||||
| import { a as c } from 'test-a'; |  | ||||||
| import d from 'test-d'; |  | ||||||
| import 'test-c'; |  | ||||||
| 
 |  | ||||||
| export function pageConfig() { |  | ||||||
|   return { a: 1 }; |  | ||||||
| } |  | ||||||
|  | @ -1,11 +0,0 @@ | ||||||
| import React, { useState, useEffect } from 'react'; |  | ||||||
| 
 |  | ||||||
| export default function Bar() { |  | ||||||
|   const [str] = useState(''); |  | ||||||
|   useEffect(() => {}, []); |  | ||||||
|   return <React.Fragment>{str}</React.Fragment>; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export function pageConfig() { |  | ||||||
|   return { a: 1 }; |  | ||||||
| } |  | ||||||
|  | @ -1,7 +0,0 @@ | ||||||
| const c = {}; |  | ||||||
| const { a = '', b = '' } = c; |  | ||||||
| const d = () => { |  | ||||||
|   console.log(a, b); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export default d; |  | ||||||
|  | @ -1,5 +0,0 @@ | ||||||
| import { a, b } from 'test'; |  | ||||||
| 
 |  | ||||||
| function test() { |  | ||||||
|   a(); |  | ||||||
| } |  | ||||||
|  | @ -1,17 +0,0 @@ | ||||||
| import { a, z } from 'a'; |  | ||||||
| import b from 'b'; |  | ||||||
| import c from 'c'; |  | ||||||
| import d from 'd'; |  | ||||||
| 
 |  | ||||||
| const [e, f, ...rest] = a; |  | ||||||
| const { h, j } = b; |  | ||||||
| const [x, ...m] = c; |  | ||||||
| const zz = 'x'; |  | ||||||
| const { k, l, ...s } = d; |  | ||||||
| 
 |  | ||||||
| export function pageConfig() { |  | ||||||
|   return { |  | ||||||
|     x, |  | ||||||
|     k, |  | ||||||
|   }; |  | ||||||
| } |  | ||||||
|  | @ -1,8 +0,0 @@ | ||||||
| let j = 2; |  | ||||||
| let i = 2; |  | ||||||
| while (j < 3) { |  | ||||||
|   j++; |  | ||||||
| } |  | ||||||
| do { |  | ||||||
|   i++; |  | ||||||
| } while (i < 5); |  | ||||||
|  | @ -1,108 +0,0 @@ | ||||||
| import * as path from 'path'; |  | ||||||
| import * as fs from 'fs'; |  | ||||||
| import { fileURLToPath } from 'url'; |  | ||||||
| import { expect, it, describe } from 'vitest'; |  | ||||||
| import { parse, type ParserOptions } from '@babel/parser'; |  | ||||||
| import traverse from '@babel/traverse'; |  | ||||||
| import generate from '@babel/generator'; |  | ||||||
| import removeTopLevelCodePlugin from '../src/utils/babelPluginRemoveCode'; |  | ||||||
| 
 |  | ||||||
| const __dirname = path.dirname(fileURLToPath(import.meta.url)); |  | ||||||
| 
 |  | ||||||
| const parserOptions: ParserOptions = { |  | ||||||
|   sourceType: 'module', |  | ||||||
|   plugins: [ |  | ||||||
|     'jsx', |  | ||||||
|     'importMeta', |  | ||||||
|     'topLevelAwait', |  | ||||||
|     'classProperties', |  | ||||||
|     'classPrivateMethods', |  | ||||||
|     'typescript', |  | ||||||
|     'decorators-legacy', |  | ||||||
|   ], |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| describe('remove top level code', () => { |  | ||||||
|   it('remove specifier export', () => { |  | ||||||
|     const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/export-specifier.ts'), 'utf-8'), parserOptions); |  | ||||||
|     traverse(ast, removeTopLevelCodePlugin(['pageConfig'])); |  | ||||||
|     const content = generate(ast).code; |  | ||||||
|     expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe('const pageConfig = () => {};export { pageConfig };'); |  | ||||||
|   }); |  | ||||||
|   it('remove variable export', () => { |  | ||||||
|     const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/export-variable.ts'), 'utf-8'), parserOptions); |  | ||||||
|     traverse(ast, removeTopLevelCodePlugin(['pageConfig'])); |  | ||||||
|     const content = generate(ast).code; |  | ||||||
|     expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe('export const pageConfig = () => {};'); |  | ||||||
|   }); |  | ||||||
|   it('remove function export', () => { |  | ||||||
|     const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/function-exports.ts'), 'utf-8'), parserOptions); |  | ||||||
|     traverse(ast, removeTopLevelCodePlugin(['pageConfig'])); |  | ||||||
|     const content = generate(ast).code; |  | ||||||
|     expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe('export function pageConfig() {}'); |  | ||||||
|   }); |  | ||||||
|   it('remove if statement', () => { |  | ||||||
|     const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/if.ts'), 'utf-8'), parserOptions); |  | ||||||
|     traverse(ast, removeTopLevelCodePlugin(['pageConfig'])); |  | ||||||
|     const content = generate(ast).code; |  | ||||||
|     expect(content).toBe(''); |  | ||||||
|   }); |  | ||||||
|   it('remove import statement', () => { |  | ||||||
|     const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/import.ts'), 'utf-8'), parserOptions); |  | ||||||
|     traverse(ast, removeTopLevelCodePlugin(['pageConfig'])); |  | ||||||
|     const content = generate(ast).code; |  | ||||||
|     expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe('export function pageConfig() { return { a: 1 };}'); |  | ||||||
|   }); |  | ||||||
|   it('remove mixed import statement', () => { |  | ||||||
|     const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/mixed-import.tsx'), 'utf-8'), parserOptions); |  | ||||||
|     traverse(ast, removeTopLevelCodePlugin(['pageConfig'])); |  | ||||||
|     const content = generate(ast).code; |  | ||||||
|     expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe('export function pageConfig() { return { a: 1 };}'); |  | ||||||
|   }); |  | ||||||
|   it('remove IIFE code', () => { |  | ||||||
|     const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/iife.ts'), 'utf-8'), parserOptions); |  | ||||||
|     traverse(ast, removeTopLevelCodePlugin(['pageConfig'])); |  | ||||||
|     const content = generate(ast).code; |  | ||||||
|     expect(content).toBe(''); |  | ||||||
|   }); |  | ||||||
|   it('remove loop code', () => { |  | ||||||
|     const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/while.ts'), 'utf-8'), parserOptions); |  | ||||||
|     traverse(ast, removeTopLevelCodePlugin(['pageConfig'])); |  | ||||||
|     const content = generate(ast).code; |  | ||||||
|     expect(content).toBe(''); |  | ||||||
|   }); |  | ||||||
|   it('remove nested reference code', () => { |  | ||||||
|     const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/reference.ts'), 'utf-8'), parserOptions); |  | ||||||
|     traverse(ast, removeTopLevelCodePlugin(['pageConfig'])); |  | ||||||
|     const content = generate(ast).code; |  | ||||||
|     expect(content).toBe(''); |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   it('remove variable declaration code', () => { |  | ||||||
|     const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/vars.ts'), 'utf-8'), parserOptions); |  | ||||||
|     traverse(ast, removeTopLevelCodePlugin(['pageConfig'])); |  | ||||||
|     const content = generate(ast).code; |  | ||||||
|     expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe('import c from \'c\';import d from \'d\';const [x] = c;const { k} = d;export function pageConfig() { return { x, k };}'); |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   it('keep export default', () => { |  | ||||||
|     const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/export-default.ts'), 'utf-8'), parserOptions); |  | ||||||
|     traverse(ast, removeTopLevelCodePlugin(['default'])); |  | ||||||
|     const content = generate(ast).code; |  | ||||||
|     expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe('const a = 1;export default a;'); |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   it('remove expression statement', () => { |  | ||||||
|     const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/expression.ts'), 'utf-8'), parserOptions); |  | ||||||
|     traverse(ast, removeTopLevelCodePlugin(['default'])); |  | ||||||
|     const content = generate(ast).code; |  | ||||||
|     expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe(''); |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   it('remove nested reference code', () => { |  | ||||||
|     const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/properties.ts'), 'utf-8'), parserOptions); |  | ||||||
|     traverse(ast, removeTopLevelCodePlugin(['pageConfig'])); |  | ||||||
|     const content = generate(ast).code; |  | ||||||
|     expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe(''); |  | ||||||
|   }); |  | ||||||
| }); |  | ||||||
|  | @ -39,7 +39,7 @@ type Experimental = Configuration['experiments']; | ||||||
| interface SwcOptions { | interface SwcOptions { | ||||||
|   removeExportExprs?: string[]; |   removeExportExprs?: string[]; | ||||||
|   compilationConfig?: SwcCompilationConfig | ((source: string, id: string) => SwcCompilationConfig); |   compilationConfig?: SwcCompilationConfig | ((source: string, id: string) => SwcCompilationConfig); | ||||||
|   keepExports?: string[]; |   keepExports?: string[] | { value: string[]; include?: (id: string) => boolean }; | ||||||
|   nodeTransform?: boolean; |   nodeTransform?: boolean; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -114,23 +114,22 @@ const compilationPlugin = (options: Options): UnpluginOptions => { | ||||||
|           ]); |           ]); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
| 
 |  | ||||||
|       if (keepExports) { |       if (keepExports) { | ||||||
|         if (isRouteEntry(id)) { |         const keepList = Array.isArray(keepExports) ? keepExports : keepExports.value; | ||||||
|           swcPlugins.push([ |         const customInlcude = !Array.isArray(keepExports) && keepExports?.include; | ||||||
|             swcPluginKeepExport, |         let matchRule = false; | ||||||
|             keepExports, |         if (customInlcude) { | ||||||
|           ]); |           matchRule = customInlcude(id); | ||||||
|         } else if (isAppEntry(id)) { |         } else { | ||||||
|           let keepList; |           const matchRoute = isRouteEntry(id); | ||||||
| 
 |           const matchEntry = isAppEntry(id); | ||||||
|           if (keepExports.indexOf('pageConfig') > -1) { |           if (matchEntry && keepList.indexOf('pageConfig') > -1) { | ||||||
|             // when build for pageConfig, should keep default, it equals to getAppConfig
 |             // when build for pageConfig, should keep default, it equals to getAppConfig
 | ||||||
|             keepList = keepExports.concat(['default']); |             keepList.push('default'); | ||||||
|           } else { |  | ||||||
|             keepList = keepExports; |  | ||||||
|           } |           } | ||||||
| 
 |           matchRule = matchRoute || matchEntry; | ||||||
|  |         } | ||||||
|  |         if (matchRule) { | ||||||
|           swcPlugins.push([ |           swcPlugins.push([ | ||||||
|             swcPluginKeepExport, |             swcPluginKeepExport, | ||||||
|             keepList, |             keepList, | ||||||
|  |  | ||||||
|  | @ -800,7 +800,7 @@ importers: | ||||||
| 
 | 
 | ||||||
|   packages/bundles: |   packages/bundles: | ||||||
|     specifiers: |     specifiers: | ||||||
|       '@ice/swc-plugin-keep-export': 0.1.4 |       '@ice/swc-plugin-keep-export': 0.1.4-2 | ||||||
|       '@ice/swc-plugin-node-transform': 0.1.0-5 |       '@ice/swc-plugin-node-transform': 0.1.0-5 | ||||||
|       '@ice/swc-plugin-remove-export': 0.1.2 |       '@ice/swc-plugin-remove-export': 0.1.2 | ||||||
|       '@pmmmwh/react-refresh-webpack-plugin': 0.5.10 |       '@pmmmwh/react-refresh-webpack-plugin': 0.5.10 | ||||||
|  | @ -879,7 +879,7 @@ importers: | ||||||
|       webpack-dev-server: 4.11.1 |       webpack-dev-server: 4.11.1 | ||||||
|       ws: ^8.4.2 |       ws: ^8.4.2 | ||||||
|     dependencies: |     dependencies: | ||||||
|       '@ice/swc-plugin-keep-export': 0.1.4 |       '@ice/swc-plugin-keep-export': 0.1.4-2 | ||||||
|       '@ice/swc-plugin-node-transform': 0.1.0-5 |       '@ice/swc-plugin-node-transform': 0.1.0-5 | ||||||
|       '@ice/swc-plugin-remove-export': 0.1.2 |       '@ice/swc-plugin-remove-export': 0.1.2 | ||||||
|       '@swc/core': 1.3.19 |       '@swc/core': 1.3.19 | ||||||
|  | @ -980,10 +980,6 @@ importers: | ||||||
| 
 | 
 | ||||||
|   packages/ice: |   packages/ice: | ||||||
|     specifiers: |     specifiers: | ||||||
|       '@babel/generator': 7.18.10 |  | ||||||
|       '@babel/parser': 7.18.10 |  | ||||||
|       '@babel/traverse': 7.18.10 |  | ||||||
|       '@babel/types': 7.18.10 |  | ||||||
|       '@ice/bundles': 0.1.8 |       '@ice/bundles': 0.1.8 | ||||||
|       '@ice/route-manifest': 1.1.1 |       '@ice/route-manifest': 1.1.1 | ||||||
|       '@ice/runtime': ^1.1.5 |       '@ice/runtime': ^1.1.5 | ||||||
|  | @ -1010,7 +1006,6 @@ importers: | ||||||
|       dotenv-expand: ^8.0.3 |       dotenv-expand: ^8.0.3 | ||||||
|       ejs: ^3.1.6 |       ejs: ^3.1.6 | ||||||
|       esbuild: ^0.16.5 |       esbuild: ^0.16.5 | ||||||
|       estree-walker: ^3.0.2 |  | ||||||
|       fast-glob: ^3.2.11 |       fast-glob: ^3.2.11 | ||||||
|       find-up: ^5.0.0 |       find-up: ^5.0.0 | ||||||
|       fs-extra: ^10.0.0 |       fs-extra: ^10.0.0 | ||||||
|  | @ -1032,10 +1027,6 @@ importers: | ||||||
|       webpack-dev-server: ^4.7.4 |       webpack-dev-server: ^4.7.4 | ||||||
|       yargs-parser: ^21.1.1 |       yargs-parser: ^21.1.1 | ||||||
|     dependencies: |     dependencies: | ||||||
|       '@babel/generator': 7.18.10 |  | ||||||
|       '@babel/parser': 7.18.10 |  | ||||||
|       '@babel/traverse': 7.18.10 |  | ||||||
|       '@babel/types': 7.18.10 |  | ||||||
|       '@ice/bundles': link:../bundles |       '@ice/bundles': link:../bundles | ||||||
|       '@ice/route-manifest': link:../route-manifest |       '@ice/route-manifest': link:../route-manifest | ||||||
|       '@ice/runtime': link:../runtime |       '@ice/runtime': link:../runtime | ||||||
|  | @ -1052,7 +1043,6 @@ importers: | ||||||
|       dotenv: 16.0.3 |       dotenv: 16.0.3 | ||||||
|       dotenv-expand: 8.0.3 |       dotenv-expand: 8.0.3 | ||||||
|       ejs: 3.1.8 |       ejs: 3.1.8 | ||||||
|       estree-walker: 3.0.3 |  | ||||||
|       fast-glob: 3.2.12 |       fast-glob: 3.2.12 | ||||||
|       find-up: 5.0.0 |       find-up: 5.0.0 | ||||||
|       fs-extra: 10.1.0 |       fs-extra: 10.1.0 | ||||||
|  | @ -5187,8 +5177,8 @@ packages: | ||||||
|       - react-native |       - react-native | ||||||
|     dev: false |     dev: false | ||||||
| 
 | 
 | ||||||
|   /@ice/swc-plugin-keep-export/0.1.4: |   /@ice/swc-plugin-keep-export/0.1.4-2: | ||||||
|     resolution: {integrity: sha512-fOc09KALmL2zJK1xNGTEt/C27mXL7NVn/v1eRjjuM4uer+qmWIxYXIa9dpfTX5ZUn8zXhrKH8lGdczoKHCzyQQ==} |     resolution: {integrity: sha512-kmQms1GTc4LBfPK+SyEo3UBBX0GMhPB02VJPA34AtIpNmaWApgmkqQBHbmeeV8ad0nrHrxTfRL80ldMhplyC4g==} | ||||||
|     dev: false |     dev: false | ||||||
| 
 | 
 | ||||||
|   /@ice/swc-plugin-node-transform/0.1.0-5: |   /@ice/swc-plugin-node-transform/0.1.0-5: | ||||||
|  | @ -11130,12 +11120,6 @@ packages: | ||||||
|   /estree-walker/2.0.2: |   /estree-walker/2.0.2: | ||||||
|     resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} |     resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} | ||||||
| 
 | 
 | ||||||
|   /estree-walker/3.0.3: |  | ||||||
|     resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} |  | ||||||
|     dependencies: |  | ||||||
|       '@types/estree': 1.0.0 |  | ||||||
|     dev: false |  | ||||||
| 
 |  | ||||||
|   /esutils/2.0.3: |   /esutils/2.0.3: | ||||||
|     resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} |     resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} | ||||||
|     engines: {node: '>=0.10.0'} |     engines: {node: '>=0.10.0'} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue