diff --git a/docs/HOW_TO_CREATE_LIBRARY.md b/docs/HOW_TO_CREATE_LIBRARY.md new file mode 100644 index 00000000..e47cde0f --- /dev/null +++ b/docs/HOW_TO_CREATE_LIBRARY.md @@ -0,0 +1,95 @@ +# How to create Library + +Library authors can use kaze-style to create pre-generated CSS in JS. + +In kaze-style, there are two ways to create a Library. + +## 1. Static assets + +type reference [style](./1.STYLE.md) | [globalStyle](./2.GLOBAL_STYLE.md) + +```ts +// Example +// @kaze-style/themes theme & globalTheme uses this method. + +import { style, globalStyle } from '@kaze-style/core'; +import { theme, globalTheme } from '@kaze-style/themes'; + +globalStyle(globalTheme.reset()); + +export const classes = style({ + container: { + ...theme.animation('spin'), + }, +}); +``` + +### how to create + +```ts +// Example +export const animation = { + spin: { + animationDuration: '1s', + animationIterationCount: 'infinite', + animationTimingFunction: 'linear', + animationName: { + from: { + transform: 'rotate(0deg)', + }, + to: { + transform: 'rotate(360deg)', + }, + }, + }, +} as const; +``` + +## 2. Function API + +This one can do more than static assets, but is a bit more complicated. + +```ts +// Example +// @kaze-style/core style & globalStyle uses this method. + +import { cssVar } from 'your-lib'; + +export const vars = cssVar({ + darkColor: 'black', + lightColor: 'white', +}); +``` + +### how to create + +```ts +// Example +// @kaze-style/core style api +import { isBuildTime } from './isBuildTime'; +import { resolveStyle } from './resolveStyle'; +import { setCssRules } from './setCssRules'; +import type { BuildArg, Classes } from './types/common'; +import type { KazeStyle } from './types/style'; +import { classesSerialize } from './utils/classesSerialize'; + +export function style(styles: KazeStyle): Classes; +export function style( + styles: KazeStyle, + buildArg: BuildArg, + index: number, +): Classes; +export function style( + styles: KazeStyle, + buildArg?: BuildArg, + index?: number, +): Classes { + const [cssRules, classes, staticClasses] = resolveStyle(styles); + if (isBuildTime(buildArg) && typeof index !== 'undefined') { + const classesNode = classesSerialize(staticClasses); + buildArg.injector.cssRules.push(...cssRules); + buildArg.injector.args.push({ value: [classesNode], index }); + } else if (typeof document !== 'undefined') setCssRules(cssRules); + return classes; +} +``` diff --git a/packages/babel-plugin/src/astNode.ts b/packages/babel-plugin/src/astNode.ts new file mode 100644 index 00000000..4dfdd138 --- /dev/null +++ b/packages/babel-plugin/src/astNode.ts @@ -0,0 +1,47 @@ +import type { types as t } from '@babel/core'; +import type { AstNode } from '@kaze-style/core'; + +export const nodeToExpr = (node: AstNode): t.Expression => { + if (node.type === 'String') { + return { type: 'StringLiteral', value: node.value }; + } else if (node.type === 'Number') { + return { type: 'NumericLiteral', value: node.value }; + } else if (node.type === 'Boolean') { + return { type: 'BooleanLiteral', value: node.value }; + } else if (node.type === 'Null') { + return { type: 'NullLiteral' }; + } else if (node.type === 'Identifier') { + return { + type: 'Identifier', + name: node.name, + }; + } else if (node.type === 'Call') { + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: node.name, + }, + arguments: node.arguments.map((value) => nodeToExpr(value)), + }; + } else if (node.type === 'Array') { + return { + type: 'ArrayExpression', + elements: node.elements.map((value) => nodeToExpr(value)), + }; + } else { + return { + type: 'ObjectExpression', + properties: node.properties.map(({ key, value }) => ({ + type: 'ObjectProperty', + key: { + type: 'StringLiteral', + value: key, + }, + value: nodeToExpr(value), + computed: false, + shorthand: false, + })), + }; + } +}; diff --git a/packages/babel-plugin/src/commonConfig.ts b/packages/babel-plugin/src/commonConfig.ts new file mode 100644 index 00000000..ad3691f9 --- /dev/null +++ b/packages/babel-plugin/src/commonConfig.ts @@ -0,0 +1,5 @@ +export type InputTransform = { + source: string; + from: string; + to: string; +}; diff --git a/packages/babel-plugin/src/index.ts b/packages/babel-plugin/src/index.ts index 02aaca70..3efc25b9 100644 --- a/packages/babel-plugin/src/index.ts +++ b/packages/babel-plugin/src/index.ts @@ -2,6 +2,4 @@ export * from './preTransform'; export * from './transform'; export * from './preTransformPlugin'; export * from './transformPlugin'; -export type { PreTransformOptions } from './preTransformPlugin'; -export type { TransformOptions } from './transformPlugin'; export type { TransformOptions as BabelOptions } from '@babel/core'; diff --git a/packages/babel-plugin/src/preTransform.ts b/packages/babel-plugin/src/preTransform.ts index 6e9db869..38e4d53a 100644 --- a/packages/babel-plugin/src/preTransform.ts +++ b/packages/babel-plugin/src/preTransform.ts @@ -2,13 +2,12 @@ import { transformAsync as babelTransform } from '@babel/core'; import type { TransformOptions as BabelOptions } from '@babel/core'; // @ts-expect-error type import typescriptSyntax from '@babel/plugin-syntax-typescript'; -import type { PreTransformOptions } from './preTransformPlugin'; import { preTransformPlugin } from './preTransformPlugin'; type Options = { filename: string; - preTransformOptions: PreTransformOptions; - babelOptions?: BabelOptions; + transform: Record; + babel?: BabelOptions; }; type Metadata = { isTransformed: boolean }; @@ -16,17 +15,19 @@ type Result = [string, Metadata]; export const preTransform = async ( code: string, - { filename, preTransformOptions, babelOptions = {} }: Options, + options: Options, ): Promise => { + const babelOptions = options.babel || {}; + const transformOptions = options.transform || {}; const result = await babelTransform(code, { - filename, + filename: options.filename, caller: { name: 'kaze' }, babelrc: false, configFile: false, compact: false, ...babelOptions, plugins: [ - [preTransformPlugin, preTransformOptions], + [preTransformPlugin, transformOptions], [typescriptSyntax, { isTSX: true }], ...(babelOptions.plugins || []), ], diff --git a/packages/babel-plugin/src/preTransformPlugin.ts b/packages/babel-plugin/src/preTransformPlugin.ts index ab46dcb3..de96e741 100644 --- a/packages/babel-plugin/src/preTransformPlugin.ts +++ b/packages/babel-plugin/src/preTransformPlugin.ts @@ -1,98 +1,132 @@ -import { types as t, template } from '@babel/core'; -import type { NodePath, PluginObj, PluginPass } from '@babel/core'; +import { types as t } from '@babel/core'; +import type { PluginObj, PluginPass } from '@babel/core'; import { declare } from '@babel/helper-plugin-utils'; +import type { AstNode } from '@kaze-style/core'; +import { nodeToExpr } from './astNode'; +import type { InputTransform } from './commonConfig'; + +type InputConfig = { + transforms: InputTransform[]; + buildArg: AstNode; +}; type Transform = { from: string; - to: string; + importSource: string; + identNames: string[]; + namespaces: string[]; }; type State = { - targetPaths?: Array<{ - callee: NodePath; - definition: NodePath; - transform: Transform; - }>; -}; - -const options = { - importSource: '@kaze-style/core', - transforms: [ - { - from: 'style', - to: '__preStyle', - }, - { - from: 'globalStyle', - to: '__preGlobalStyle', - }, - ], -}; - -const buildPreStyleImport = template(` - import { ${options.transforms - .map((transform) => transform.to) - .join(',')} } from '${options.importSource}'; -`); - -export type PreTransformOptions = { - filename: string; - forBuildName: string; + isUseNameSpace?: boolean; + transforms?: Transform[]; + buildArg?: t.Expression; + callExprs?: t.CallExpression[]; }; export const preTransformPlugin = declare< - PreTransformOptions, - PluginObj ->((_, { filename, forBuildName }) => { + InputConfig, + PluginObj +>((_, config) => { return { name: '@kaze-style/babel-plugin-preTransform', pre() { - this.targetPaths = []; + this.transforms = config.transforms.map((transform) => ({ + from: transform.from, + importSource: transform.source, + identNames: [], + namespaces: [], + })); + this.isUseNameSpace = false; + this.callExprs = []; + this.buildArg = nodeToExpr(config.buildArg); }, visitor: { Program: { - exit(path, state) { - if (state.targetPaths && state.targetPaths.length !== 0) { - state.targetPaths.forEach( - ({ callee, definition, transform }, index) => { - const callExpressionPath = definition.findParent((parentPath) => - parentPath.isCallExpression(), - ) as NodePath; - if (callExpressionPath.node.arguments[0]) { - callExpressionPath.node.arguments = [ - callExpressionPath.node.arguments[0], - t.identifier(forBuildName), - t.stringLiteral(filename), - t.numericLiteral(index), - ]; + enter(path, state) { + path.node.body.forEach((statement) => { + if (t.isImportDeclaration(statement)) { + state.transforms?.forEach((transform) => { + if (transform.importSource === statement.source.value) { + statement.specifiers.forEach((specifier) => { + if (t.isImportSpecifier(specifier)) { + if (t.isIdentifier(specifier.imported)) { + if (transform.from === specifier.imported.name) { + transform.identNames.push(specifier.local.name); + } + } + } else if (t.isImportNamespaceSpecifier(specifier)) { + state.isUseNameSpace = true; + transform.namespaces.push(specifier.local.name); + } + }); } - callee.replaceWith(t.identifier(transform.to)); - }, - ); - path.unshiftContainer('body', buildPreStyleImport()); - this.file.metadata = { isTransformed: true }; - } + }); + } + }); }, - }, - CallExpression(path, state) { - const calleePath = path.get('callee'); - options.transforms.forEach((transform) => { - if ( - calleePath.referencesImport(options.importSource, transform.from) - ) { - const argumentPaths = path.get('arguments') as NodePath[]; - if (Array.isArray(argumentPaths)) { - const definitionsPath = argumentPaths[0]; - if (definitionsPath !== undefined) { - state.targetPaths?.push({ - callee: calleePath as NodePath, - definition: definitionsPath, - transform: transform, + exit(_path, state) { + let index = 0; + state.callExprs?.forEach((callExpr) => { + state.transforms?.forEach((transform) => { + let isTarget = false; + const callee = callExpr.callee; + if (t.isIdentifier(callee)) { + transform.identNames.forEach((identName) => { + if (identName === callee.name) { + isTarget = true; + } }); + } else if (t.isMemberExpression(callee)) { + const obj = callee.object; + if (t.isIdentifier(obj)) { + transform.namespaces.forEach((namespace) => { + if (namespace === obj.name) { + const prop = callee.property; + if (t.isIdentifier(prop)) { + transform.identNames.forEach((identName) => { + if (prop.name === identName) { + isTarget = true; + } + }); + } + } + }); + } } - } + if (isTarget) { + if (state.buildArg) { + callExpr.arguments.push(state.buildArg); + callExpr.arguments.push(t.valueToNode(index)); + index += 1; + this.file.metadata = { isTransformed: true }; + } + } + }); + }); + }, + }, + MemberExpression(path, state) { + if (state.isUseNameSpace) { + const obj = path.node.object; + if (t.isIdentifier(obj)) { + state.transforms?.forEach((transform) => { + transform.namespaces.forEach((namespace) => { + if (namespace === obj.name) { + const prop = path.node.property; + if (t.isIdentifier(prop)) { + if (prop.name === transform.from) { + transform.identNames.push(prop.name); + } + } + } + }); + }); } - }); + } + }, + CallExpression(path, state) { + state.callExprs?.push(path.node); }, }, }; diff --git a/packages/babel-plugin/src/transform.ts b/packages/babel-plugin/src/transform.ts index 334b3977..d1b71949 100644 --- a/packages/babel-plugin/src/transform.ts +++ b/packages/babel-plugin/src/transform.ts @@ -2,13 +2,12 @@ import { transformAsync as babelTransform } from '@babel/core'; import type { TransformOptions as BabelOptions } from '@babel/core'; // @ts-expect-error type import typescriptSyntax from '@babel/plugin-syntax-typescript'; -import type { TransformOptions } from './transformPlugin'; import { transformPlugin } from './transformPlugin'; type Options = { filename: string; - transformOptions: TransformOptions; - babelOptions?: BabelOptions; + transform: Record; + babel?: BabelOptions; }; type Metadata = undefined; @@ -16,10 +15,12 @@ type Result = [string, Metadata]; export const transform = async ( code: string, - { filename, transformOptions, babelOptions = {} }: Options, + options: Options, ): Promise => { + const babelOptions = options.babel || {}; + const transformOptions = options.transform || {}; const result = await babelTransform(code, { - filename, + filename: options.filename, caller: { name: 'kaze' }, babelrc: false, configFile: false, diff --git a/packages/babel-plugin/src/transformPlugin.ts b/packages/babel-plugin/src/transformPlugin.ts index de5419e5..563c9a51 100644 --- a/packages/babel-plugin/src/transformPlugin.ts +++ b/packages/babel-plugin/src/transformPlugin.ts @@ -1,124 +1,168 @@ -import { types as t, template } from '@babel/core'; -import type { NodePath, PluginObj, PluginPass } from '@babel/core'; +import { types as t } from '@babel/core'; +import type { PluginObj, PluginPass } from '@babel/core'; import { declare } from '@babel/helper-plugin-utils'; -import type { ForBuild } from '@kaze-style/core'; +import type { AstNode } from '@kaze-style/core'; +import { nodeToExpr } from './astNode'; +import type { InputTransform } from './commonConfig'; -type Transform = { - from: string; - to: string; +type InputConfig = { + transforms: InputTransform[]; + injectArgs: InputArgument[]; + imports: InputImport[]; }; -type State = { - targetPaths?: Array<{ - callee: NodePath; - definition: NodePath; - transform: Transform; - }>; +type InputArgument = { + value: AstNode[]; + index: number; }; -const options = { - importSource: '@kaze-style/core', - transforms: [ - { - from: '__preStyle', - to: '__style', - }, - { - from: '__preGlobalStyle', - to: '__globalStyle', - }, - ], +type InputImport = { + source: string; + specifier: string; +}; + +type Transform = { + to: string; + from: string; + importSource: string; + identNames: string[]; + namespaces: string[]; }; -const buildStyleImport = template(` - import { ${options.transforms - .map((transform) => transform.to) - .join(',')} , ClassName } from '${options.importSource}'; -`); +type ArgumentExpression = { + value: t.Expression[]; + index: number; +}; -export type TransformOptions = { - styles: ForBuild[2]; +type State = { + isUseNameSpace?: boolean; + transforms?: Transform[]; + injectArgs?: ArgumentExpression[]; + callExprs?: t.CallExpression[]; }; export const transformPlugin = declare< - TransformOptions, - PluginObj ->((_, { styles }) => { + InputConfig, + PluginObj +>((_, config) => { return { name: '@kaze-style/babel-plugin-transform', pre() { - this.targetPaths = []; + this.transforms = config.transforms.map((transform) => ({ + to: transform.to, + from: transform.from, + importSource: transform.source, + identNames: [], + namespaces: [], + })); + this.isUseNameSpace = false; + this.callExprs = []; + this.injectArgs = config.injectArgs.map((injectArg) => ({ + value: injectArg.value.map((value) => nodeToExpr(value)), + index: injectArg.index, + })); }, visitor: { Program: { - exit(path, state) { - const _styles = styles.map(([classes, index]) => ({ - classes, - index, - })); - if (state.targetPaths && state.targetPaths.length !== 0) { - state.targetPaths.forEach(({ callee, definition, transform }) => { - const callExpressionPath = definition.findParent((parentPath) => - parentPath.isCallExpression(), - ) as NodePath; - const indexArgPath = callExpressionPath.node - .arguments[3] as t.NumericLiteral; - const classes = _styles.find( - (style) => style.index === indexArgPath.value, - )?.classes; - const objectProperties: t.ObjectProperty[] = []; - for (const key in classes) { - if (classes.hasOwnProperty(key)) { - const className = classes[key]; - if (typeof className === 'string') { - objectProperties.push( - t.objectProperty( - t.stringLiteral(key), - t.stringLiteral(className), - ), - ); + enter(path, state) { + path.node.body.forEach((statement) => { + if (t.isImportDeclaration(statement)) { + state.transforms?.forEach((transform) => { + if (transform.importSource === statement.source.value) { + statement.specifiers.forEach((specifier) => { + if (t.isImportSpecifier(specifier)) { + if (t.isIdentifier(specifier.imported)) { + if (transform.from === specifier.imported.name) { + specifier.imported.name = transform.to; + transform.identNames.push(specifier.local.name); + } + } + } else if (t.isImportNamespaceSpecifier(specifier)) { + state.isUseNameSpace = true; + transform.namespaces.push(specifier.local.name); + } + }); + } + }); + } + }); + }, + exit(_path, state) { + state.callExprs?.forEach((callExpr) => { + state.transforms?.forEach((transform) => { + let isTarget = false; + const callee = callExpr.callee; + if (t.isIdentifier(callee)) { + transform.identNames.forEach((identName) => { + if (identName === callee.name) { + isTarget = true; + } + }); + } else if (t.isMemberExpression(callee)) { + const obj = callee.object; + if (t.isIdentifier(obj)) { + transform.namespaces.forEach((namespace) => { + if (namespace === obj.name) { + const prop = callee.property; + if (t.isIdentifier(prop)) { + transform.identNames.forEach((identName) => { + if (prop.name === identName) { + isTarget = true; + } + }); + } + } + }); + } + } + if (isTarget) { + if (state.injectArgs) { + const lastArg = callExpr.arguments.at(-1); + if (t.isNumericLiteral(lastArg)) { + let is_transform = false; + for (const injectArg of state.injectArgs || []) { + if (lastArg.value === injectArg.index) { + callExpr.arguments = injectArg.value; + is_transform = true; + break; + } + } + if (is_transform === false) { + callExpr.arguments = []; + } } else { - objectProperties.push( - t.objectProperty( - t.stringLiteral(key), - t.newExpression(t.identifier('ClassName'), [ - t.valueToNode(classes[key] || {}), - ]), - ), - ); + callExpr.arguments = []; } + } else { + callExpr.arguments = []; } } - callExpressionPath.node.arguments = [ - t.objectExpression(objectProperties), - ]; - callee.replaceWith(t.identifier(transform.to)); }); - - path.unshiftContainer('body', buildStyleImport()); - this.file.metadata = { transformed: true }; - } + }); }, }, - CallExpression(path, state) { - const calleePath = path.get('callee'); - options.transforms.forEach((transform) => { - if ( - calleePath.referencesImport(options.importSource, transform.from) - ) { - const argumentPaths = path.get('arguments') as NodePath[]; - if (Array.isArray(argumentPaths) && argumentPaths.length === 4) { - const definitionsPath = argumentPaths[0]; - if (definitionsPath !== undefined) { - state.targetPaths?.push({ - callee: calleePath as NodePath, - definition: definitionsPath, - transform: transform, - }); - } - } + MemberExpression(path, state) { + if (state.isUseNameSpace) { + const obj = path.node.object; + if (t.isIdentifier(obj)) { + state.transforms?.forEach((transform) => { + transform.namespaces.forEach((namespace) => { + if (namespace === obj.name) { + const prop = path.node.property; + if (t.isIdentifier(prop)) { + if (prop.name === transform.from) { + prop.name = transform.to; + transform.identNames.push(prop.name); + } + } + } + }); + }); } - }); + } + }, + CallExpression(path, state) { + state.callExprs?.push(path.node); }, }, }; diff --git a/packages/build/src/constants.ts b/packages/build/src/constants.ts index 40e801a0..612e1d6a 100644 --- a/packages/build/src/constants.ts +++ b/packages/build/src/constants.ts @@ -1,2 +1,25 @@ export const LAYER_PREFIX = 'kaze-'; -export const FOR_BUILD_NAME = '__forBuildByKazeStyle'; +export const BUILD_INJECTOR_NAME = '__BUILD_INJECTOR_NAME'; + +export const DEFAULT_TRANSFORMS = [ + { + from: 'style', + to: '__style', + source: '@kaze-style/core', + }, + { + from: 'globalStyle', + to: '__globalStyle', + source: '@kaze-style/core', + }, + { + from: 'createStyle', + to: '__style', + source: '@kaze-style/core', + }, + { + from: 'createGlobalStyle', + to: '__globalStyle', + source: '@kaze-style/core', + }, +]; diff --git a/packages/build/src/extractionStyle.ts b/packages/build/src/extractionStyle.ts index bb7e58d1..e2b63e52 100644 --- a/packages/build/src/extractionStyle.ts +++ b/packages/build/src/extractionStyle.ts @@ -1,38 +1,38 @@ -import type { ForBuild } from '@kaze-style/core'; +import type { Injector } from '@kaze-style/core'; import evalCode from 'eval'; -import { FOR_BUILD_NAME } from './constants'; +import { BUILD_INJECTOR_NAME } from './constants'; type Options = { filename: string; - forBuildName?: string; + buildInjectorName?: string; }; export const extractionStyle = ( code: string, - { filename, forBuildName = FOR_BUILD_NAME }: Options, + { filename, buildInjectorName = BUILD_INJECTOR_NAME }: Options, ) => { - const forBuild: ForBuild = [filename, [], []]; + const injector: Injector = { filename, args: [], cssRules: [] }; // remove start - const window = {}; - const cjsGlobal = {}; + // const window = {}; + // const cjsGlobal = {}; - if (typeof __dirname !== 'undefined') { - Object.assign(cjsGlobal, { __dirname }); - } - if (typeof __filename !== 'undefined') { - Object.assign(cjsGlobal, { __filename }); - } + // if (typeof __dirname !== 'undefined') { + // Object.assign(cjsGlobal, { __dirname }); + // } + // if (typeof __filename !== 'undefined') { + // Object.assign(cjsGlobal, { __filename }); + // } // remove end try { evalCode( code, filename, { - [forBuildName]: forBuild, + [buildInjectorName]: injector, // remove start - window, - $RefreshReg$: () => undefined, - ...cjsGlobal, + // window, + // $RefreshReg$: () => undefined, + // ...cjsGlobal, // remove end }, true, @@ -40,7 +40,5 @@ export const extractionStyle = ( } catch (error) { throw error; } - - const [, cssRules, styles] = forBuild; - return [cssRules, styles] as const; + return { injectArgs: injector.args, cssRules: injector.cssRules }; }; diff --git a/packages/build/src/index.ts b/packages/build/src/index.ts index 1a92b882..ad4ee5e7 100644 --- a/packages/build/src/index.ts +++ b/packages/build/src/index.ts @@ -3,3 +3,4 @@ export * from './cssRulesToCssString'; export * from './extractionStyle'; export * from './preTransform'; export * from './transform'; +export * from './constants'; diff --git a/packages/build/src/preTransform.ts b/packages/build/src/preTransform.ts index 5ff68533..fb598904 100644 --- a/packages/build/src/preTransform.ts +++ b/packages/build/src/preTransform.ts @@ -1,45 +1,69 @@ -import type { - PreTransformOptions, - BabelOptions, -} from '@kaze-style/babel-plugin'; +import type { BabelOptions } from '@kaze-style/babel-plugin'; import { preTransform as babelPreTransform } from '@kaze-style/babel-plugin'; +import type { AstNode } from '@kaze-style/core'; import type { SwcOptions } from '@kaze-style/swc-plugin'; import { preTransform as swcPreTransform } from '@kaze-style/swc-plugin'; -import { FOR_BUILD_NAME } from './constants'; +import { BUILD_INJECTOR_NAME } from './constants'; +import { DEFAULT_TRANSFORMS } from './constants'; +import type { Transform } from './types'; type Options = { filename: string; - swcOptions?: SwcOptions; - babelOptions?: BabelOptions; - preTransformOptions: Omit & - Partial>; + swc?: SwcOptions; + babel?: BabelOptions; + transform: { + transforms?: Transform[]; + }; +}; + +export type PreTransformOptions = { + buildArg: AstNode; + transforms: Transform[]; }; export const preTransform = async ( code: string, - { - filename, - babelOptions = {}, - swcOptions = {}, - preTransformOptions: { - forBuildName = FOR_BUILD_NAME, - ...preTransformOptions - }, - }: Options, + options: Options, compiler: 'swc' | 'babel' = 'babel', ) => { + const transformOption: PreTransformOptions = { + buildArg: { + type: 'Object', + properties: [ + { + key: 'filename', + value: { + type: 'String', + value: options.filename, + }, + }, + { + key: 'injector', + value: { + type: 'Identifier', + name: BUILD_INJECTOR_NAME, + }, + }, + ], + }, + transforms: [ + ...DEFAULT_TRANSFORMS, + ...(options.transform.transforms || []), + ], + }; + if (compiler === 'swc') { const [transformedCode, metadata] = await swcPreTransform(code, { - filename, - swcOptions, - preTransformOptions: { forBuildName, ...preTransformOptions }, + filename: options.filename, + swc: options.swc || {}, + transform: transformOption, }); return [transformedCode, metadata] as const; } else { const [transformedCode, metadata] = await babelPreTransform(code, { - filename, - babelOptions, - preTransformOptions: { forBuildName, ...preTransformOptions }, + filename: options.filename, + babel: options.babel || {}, + transform: transformOption, }); return [transformedCode, metadata] as const; } diff --git a/packages/build/src/transform.ts b/packages/build/src/transform.ts index ea2924d0..58e30a3a 100644 --- a/packages/build/src/transform.ts +++ b/packages/build/src/transform.ts @@ -1,32 +1,56 @@ -import type { TransformOptions, BabelOptions } from '@kaze-style/babel-plugin'; +import type { BabelOptions } from '@kaze-style/babel-plugin'; import { transform as babelTransform } from '@kaze-style/babel-plugin'; +import type { Injector } from '@kaze-style/core'; import type { SwcOptions } from '@kaze-style/swc-plugin'; import { transform as swcTransform } from '@kaze-style/swc-plugin'; +import { DEFAULT_TRANSFORMS } from './constants'; +import type { Import, Transform } from './types'; type Options = { filename: string; - swcOptions?: SwcOptions; - babelOptions?: BabelOptions; - transformOptions: TransformOptions; + swc?: SwcOptions; + babel?: BabelOptions; + transform: Partial; +}; + +export type TransformOptions = { + imports: Import[]; + transforms: Transform[]; + injectArgs: Injector['args']; }; export const transform = async ( code: string, - { filename, babelOptions = {}, swcOptions = {}, transformOptions }: Options, + options: Options, compiler: 'swc' | 'babel' = 'babel', ) => { + const transformOption: TransformOptions = { + injectArgs: options.transform.injectArgs || [], + imports: [ + { + source: '@kaze-style/core', + specifier: '__className', + }, + ...(options.transform.imports || []), + ], + transforms: [ + ...DEFAULT_TRANSFORMS, + ...(options.transform.transforms || []), + ], + }; if (compiler === 'swc') { const [transformedCode, metadata] = await swcTransform(code, { - filename, - swcOptions, - transformOptions, + filename: options.filename, + swc: options.swc || {}, + transform: transformOption, }); + return [transformedCode, metadata] as const; } else { const [transformedCode, metadata] = await babelTransform(code, { - filename, - babelOptions, - transformOptions, + filename: options.filename, + babel: options.babel || {}, + transform: transformOption, }); return [transformedCode, metadata] as const; } diff --git a/packages/build/src/types.ts b/packages/build/src/types.ts new file mode 100644 index 00000000..64925188 --- /dev/null +++ b/packages/build/src/types.ts @@ -0,0 +1,10 @@ +export type Transform = { + source: string; + from: string; + to: string; +}; + +export type Import = { + source: string; + specifier: string; +}; diff --git a/packages/core/src/__className.ts b/packages/core/src/__className.ts new file mode 100644 index 00000000..8e6a9278 --- /dev/null +++ b/packages/core/src/__className.ts @@ -0,0 +1,8 @@ +import { ClassName } from './ClassName'; + +export const __className = ( + _static: ClassName['Static'], + other: ClassName['Other'] = [], +) => { + return new ClassName(_static, other); +}; diff --git a/packages/core/src/__preGlobalStyle.ts b/packages/core/src/__preGlobalStyle.ts deleted file mode 100644 index 5673aafc..00000000 --- a/packages/core/src/__preGlobalStyle.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { resolveGlobalStyle } from './resolveGlobalStyle'; -import type { ForBuild } from './types/common'; -import type { KazeGlobalStyle } from './types/style'; - -export const __preGlobalStyle = ( - globalStyles: KazeGlobalStyle, - forBuild: ForBuild, - filename: string, - _index: number, -): void => { - const [cssRules] = resolveGlobalStyle(globalStyles); - if (forBuild[0] === filename) { - forBuild[1].push(...cssRules); - } -}; diff --git a/packages/core/src/__preStyle.ts b/packages/core/src/__preStyle.ts deleted file mode 100644 index 5e2a05a6..00000000 --- a/packages/core/src/__preStyle.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { resolveStyle } from './resolveStyle'; -import type { Classes, ForBuild } from './types/common'; -import type { KazeStyle } from './types/style'; - -export const __preStyle = ( - styles: KazeStyle, - forBuild: ForBuild, - filename: string, - index: number, -): Classes => { - const [cssRules, classes, staticClasses] = resolveStyle(styles); - if (forBuild[0] === filename) { - forBuild[1].push(...cssRules); - forBuild[2].push([staticClasses, index]); - } - - return classes; -}; diff --git a/packages/core/src/compileAtomicCss.ts b/packages/core/src/compileAtomicCss.ts index add38af3..da56b94b 100644 --- a/packages/core/src/compileAtomicCss.ts +++ b/packages/core/src/compileAtomicCss.ts @@ -1,6 +1,6 @@ import type { ClassName } from './ClassName'; -import type { CssRule } from './styleOrder'; import { getStyleOrder } from './styleOrder'; +import type { CssRule } from './types/common'; import type { Selectors } from './types/common'; import type { KeyframesRules, SupportStyle } from './types/style'; import { compileCss } from './utils/compileCss'; diff --git a/packages/core/src/compileNotAtomicCss.ts b/packages/core/src/compileNotAtomicCss.ts index d18e85b6..54ffd6cc 100644 --- a/packages/core/src/compileNotAtomicCss.ts +++ b/packages/core/src/compileNotAtomicCss.ts @@ -1,4 +1,5 @@ -import type { StyleOrder, CssRule } from './styleOrder'; +import type { StyleOrder } from './styleOrder'; +import type { CssRule } from './types/common'; import type { Selectors } from './types/common'; import type { KeyframesRules, SupportStyle } from './types/style'; import { compileCss } from './utils/compileCss'; diff --git a/packages/core/src/globalStyle.ts b/packages/core/src/globalStyle.ts index 6aad0253..5f736cc1 100644 --- a/packages/core/src/globalStyle.ts +++ b/packages/core/src/globalStyle.ts @@ -1,13 +1,26 @@ +import { isBuildTime } from './isBuildTime'; import { resolveGlobalStyle } from './resolveGlobalStyle'; import { setCssRules } from './setCssRules'; +import type { BuildArg } from './types/common'; import type { KazeGlobalStyle } from './types/style'; -export const globalStyle = ( - globalStyles: KazeGlobalStyle, -): void => { - const [cssRules] = resolveGlobalStyle(globalStyles); +export function globalStyle( + globalStyles: KazeGlobalStyle, +): void; +export function globalStyle( + globalStyles: KazeGlobalStyle, + buildArg: BuildArg, + index: number, +): void; +export function globalStyle( + styles: KazeGlobalStyle, + buildArg?: BuildArg, + _index?: number, +): void { + const [cssRules] = resolveGlobalStyle(styles); + if (isBuildTime(buildArg)) { + buildArg.injector.cssRules.push(...cssRules); + } else if (typeof document !== 'undefined') setCssRules(cssRules); +} - if (typeof document !== 'undefined') { - setCssRules(cssRules); - } -}; +export const createGlobalStyle = globalStyle; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4b437aa8..67af8fc3 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,21 +1,34 @@ -export * from './mergeStyle'; -export * from './style'; -export * from './globalStyle'; -export * from './resolveStyle'; -export * from './resolveGlobalStyle'; -export * from './__style'; -export * from './__globalStyle'; -export * from './__preStyle'; -export * from './__preGlobalStyle'; -export * from './ClassName'; -export * from './styleOrder'; -export * from './sortCssRules'; -export * from './uniqueCssRules'; -export * from './setCssRules'; +export { mergeStyle } from './mergeStyle'; +export { style, createStyle } from './style'; +export { globalStyle, createGlobalStyle } from './globalStyle'; +export { resolveStyle } from './resolveStyle'; +export { resolveGlobalStyle } from './resolveGlobalStyle'; +export { __style } from './__style'; +export { __globalStyle } from './__globalStyle'; +export { __className } from './__className'; +export { ClassName } from './ClassName'; +export { styleOrder, getStyleOrder } from './styleOrder'; +export type { StyleOrder } from './styleOrder'; +export { sortCssRules } from './sortCssRules'; +export { uniqueCssRules } from './uniqueCssRules'; +export { setCssRules } from './setCssRules'; +export { isBuildTime } from './isBuildTime'; export type { Classes, ClassNameOverride, ClassOverride, - ForBuild, + CssRule, } from './types/common'; +export type { Injector, BuildArg } from './types/common'; +export type { + AstNode, + StringLiteral, + NumberLiteral, + BooleanLiteral, + NullLiteral, + Identifier, + ArrayExpression, + CallExpression, + ObjectExpression, +} from './types/ast'; export type { KazeStyle, KazeGlobalStyle, SupportStyle } from './types/style'; diff --git a/packages/core/src/isBuildTime.ts b/packages/core/src/isBuildTime.ts new file mode 100644 index 00000000..c746ec99 --- /dev/null +++ b/packages/core/src/isBuildTime.ts @@ -0,0 +1,7 @@ +import type { BuildArg } from './types/common'; + +export const isBuildTime = ( + buildArg: BuildArg | undefined, +): buildArg is BuildArg => { + return buildArg ? buildArg.filename === buildArg.injector.filename : false; +}; diff --git a/packages/core/src/resolveGlobalStyle.ts b/packages/core/src/resolveGlobalStyle.ts index 19c19ed0..76651e3d 100644 --- a/packages/core/src/resolveGlobalStyle.ts +++ b/packages/core/src/resolveGlobalStyle.ts @@ -1,5 +1,5 @@ import { compileNotAtomicCss } from './compileNotAtomicCss'; -import type { CssRule } from './styleOrder'; +import type { CssRule } from './types/common'; import type { KazeGlobalStyle } from './types/style'; import { uniqueCssRules } from './uniqueCssRules'; diff --git a/packages/core/src/resolveStyle.ts b/packages/core/src/resolveStyle.ts index 5ca3a5e9..e8e10143 100644 --- a/packages/core/src/resolveStyle.ts +++ b/packages/core/src/resolveStyle.ts @@ -1,8 +1,7 @@ import { ClassName } from './ClassName'; import { compileAtomicCss } from './compileAtomicCss'; import { compileNotAtomicCss } from './compileNotAtomicCss'; -import type { CssRule } from './styleOrder'; -import type { Classes, StaticClasses } from './types/common'; +import type { Classes, StaticClasses, CssRule } from './types/common'; import type { KazeStyle } from './types/style'; import { uniqueCssRules } from './uniqueCssRules'; import { hashStyle } from './utils/hashStyle'; diff --git a/packages/core/src/setCssRules.ts b/packages/core/src/setCssRules.ts index 114f384d..ca1ba4e4 100644 --- a/packages/core/src/setCssRules.ts +++ b/packages/core/src/setCssRules.ts @@ -1,4 +1,4 @@ -import type { CssRule } from './styleOrder'; +import type { CssRule } from './types/common'; import { getStyleElements } from './utils/getStyleElements'; export const setCssRules = (cssRules: CssRule[]) => { diff --git a/packages/core/src/sortCssRules.ts b/packages/core/src/sortCssRules.ts index 6c21c443..a8859925 100644 --- a/packages/core/src/sortCssRules.ts +++ b/packages/core/src/sortCssRules.ts @@ -1,5 +1,5 @@ -import type { CssRule } from './styleOrder'; import { styleOrder } from './styleOrder'; +import type { CssRule } from './types/common'; export const sortCssRules = (cssRules: CssRule[]) => { return cssRules.sort((ruleA, ruleB) => { diff --git a/packages/core/src/style.ts b/packages/core/src/style.ts index 424ba7aa..cb5c11b7 100644 --- a/packages/core/src/style.ts +++ b/packages/core/src/style.ts @@ -1,14 +1,28 @@ +import { isBuildTime } from './isBuildTime'; import { resolveStyle } from './resolveStyle'; import { setCssRules } from './setCssRules'; -import type { Classes } from './types/common'; +import type { BuildArg, Classes } from './types/common'; import type { KazeStyle } from './types/style'; +import { classesSerialize } from './utils/classesSerialize'; -export const style = (styles: KazeStyle): Classes => { - const [cssRules, classes] = resolveStyle(styles); - - if (typeof document !== 'undefined') { - setCssRules(cssRules); - } - +export function style(styles: KazeStyle): Classes; +export function style( + styles: KazeStyle, + buildArg: BuildArg, + index: number, +): Classes; +export function style( + styles: KazeStyle, + buildArg?: BuildArg, + index?: number, +): Classes { + const [cssRules, classes, staticClasses] = resolveStyle(styles); + if (isBuildTime(buildArg) && typeof index !== 'undefined') { + const classesNode = classesSerialize(staticClasses); + buildArg.injector.cssRules.push(...cssRules); + buildArg.injector.args.push({ value: [classesNode], index }); + } else if (typeof document !== 'undefined') setCssRules(cssRules); return classes; -}; +} + +export const createStyle = style; diff --git a/packages/core/src/styleOrder.ts b/packages/core/src/styleOrder.ts index 1b9d6731..a97f6451 100644 --- a/packages/core/src/styleOrder.ts +++ b/packages/core/src/styleOrder.ts @@ -43,4 +43,3 @@ export const getStyleOrder = ([selector, atRules]: Selectors): StyleOrder => { }; export type StyleOrder = (typeof styleOrder)[number]; -export type CssRule = [value: string, order: StyleOrder]; diff --git a/packages/core/src/types/ast.ts b/packages/core/src/types/ast.ts new file mode 100644 index 00000000..e41fd4b3 --- /dev/null +++ b/packages/core/src/types/ast.ts @@ -0,0 +1,52 @@ +export type StringLiteral = { + type: 'String'; + value: string; +}; + +export type NumberLiteral = { + type: 'Number'; + value: number; +}; + +export type BooleanLiteral = { + type: 'Boolean'; + value: boolean; +}; + +export type NullLiteral = { + type: 'Null'; +}; + +export type Identifier = { + type: 'Identifier'; + name: string; +}; + +export type ArrayExpression = { + type: 'Array'; + elements: AstNode[]; +}; + +export type ObjectExpression = { + type: 'Object'; + properties: Array<{ + key: string; + value: AstNode; + }>; +}; + +export type CallExpression = { + type: 'Call'; + name: string; + arguments: AstNode[]; +}; + +export type AstNode = + | StringLiteral + | NumberLiteral + | BooleanLiteral + | NullLiteral + | Identifier + | CallExpression + | ArrayExpression + | ObjectExpression; diff --git a/packages/core/src/types/common.ts b/packages/core/src/types/common.ts index 0ff22c58..afdba03a 100644 --- a/packages/core/src/types/common.ts +++ b/packages/core/src/types/common.ts @@ -1,7 +1,10 @@ import type { ClassName } from '../ClassName'; -import type { CssRule } from '../styleOrder'; +import type { StyleOrder } from '../styleOrder'; +import type { AstNode } from './ast'; import type { FirstChar } from './utils'; +export type CssRule = [value: string, order: StyleOrder]; + export type Classes = { [P in T]: FirstChar

extends '$' ? ClassName['Type'] : string; }; @@ -25,8 +28,13 @@ export type ClassOverride = ClassNameOverride< export type Selectors = [selector: string, atRules: string[], groups: string]; -export type ForBuild = [ - filename: string, - cssRules: CssRule[], - styles: [classes: StaticClasses, index: number][], -]; +export type BuildArg = { + filename: string; + injector: Injector; +}; + +export type Injector = { + filename: string; + cssRules: CssRule[]; + args: Array<{ value: AstNode[]; index: number }>; +}; diff --git a/packages/core/src/uniqueCssRules.ts b/packages/core/src/uniqueCssRules.ts index 23b1231b..b247f01d 100644 --- a/packages/core/src/uniqueCssRules.ts +++ b/packages/core/src/uniqueCssRules.ts @@ -1,4 +1,4 @@ -import type { CssRule } from './styleOrder'; +import type { CssRule } from './types/common'; export const uniqueCssRules = (cssRules: CssRule[]) => { return Array.from( diff --git a/packages/core/src/utils/classesSerialize.ts b/packages/core/src/utils/classesSerialize.ts new file mode 100644 index 00000000..46ca5d15 --- /dev/null +++ b/packages/core/src/utils/classesSerialize.ts @@ -0,0 +1,31 @@ +import type { ObjectExpression } from '../types/ast'; +import type { StaticClasses } from '../types/common'; +import { valueToNode } from './valueToNode'; + +export const classesSerialize = ( + staticClasses: StaticClasses, +): ObjectExpression => { + const objectExpression: ObjectExpression = { + type: 'Object', + properties: [], + }; + for (const key in staticClasses) { + const className = staticClasses[key]; + if (typeof className === 'string') { + objectExpression.properties.push({ + key, + value: valueToNode(className), + }); + } else if (typeof className === 'object') { + objectExpression.properties.push({ + key, + value: { + type: 'Call', + name: '__className', + arguments: [valueToNode(className)], + }, + }); + } + } + return objectExpression; +}; diff --git a/packages/core/src/utils/valueToNode.ts b/packages/core/src/utils/valueToNode.ts new file mode 100644 index 00000000..b205068e --- /dev/null +++ b/packages/core/src/utils/valueToNode.ts @@ -0,0 +1,29 @@ +import type { AstNode } from '../types/ast'; + +export type Value = + | string + | number + | boolean + | null + | Array + | { [_ in keyof unknown]: Value }; + +export const valueToNode = (value: Value): AstNode => { + if (typeof value === 'string') return { type: 'String', value: value }; + else if (typeof value === 'number') return { type: 'Number', value: value }; + else if (typeof value === 'boolean') return { type: 'Boolean', value: value }; + else if (value === null) return { type: 'Null' }; + else if (Array.isArray(value)) + return { + type: 'Array', + elements: value.map((value) => valueToNode(value)), + }; + else + return { + type: 'Object', + properties: Object.entries(value).map(([key, value]) => ({ + key, + value: valueToNode(value as Value), + })), + }; +}; diff --git a/packages/swc-plugin/Cargo.lock b/packages/swc-plugin/Cargo.lock index 2dea43a4..efb62930 100644 --- a/packages/swc-plugin/Cargo.lock +++ b/packages/swc-plugin/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.17.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ "gimli", ] @@ -58,9 +58,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] name = "ast_node" @@ -82,7 +82,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -95,9 +95,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ "addr2line", "cc", @@ -161,9 +161,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.77" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -259,15 +259,15 @@ dependencies = [ [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "enum-iterator" -version = "1.1.3" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a0ac4aeb3a18f92eaf09c6bb9b3ac30ff61ca95514fc58cbead1c9a6bf5401" +checksum = "9ea166b3f7dc1032f7866d13f8d8e02c8d87507b61750176b86554964dc6a7bf" dependencies = [ "enum-iterator-derive", ] @@ -297,9 +297,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -366,15 +366,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.2" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" [[package]] name = "glob" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hashbrown" @@ -385,6 +385,15 @@ dependencies = [ "ahash", ] +[[package]] +name = "helper" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", + "swc_core", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -394,6 +403,15 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "hex" version = "0.4.3" @@ -443,9 +461,9 @@ dependencies = [ [[package]] name = "is-macro" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c068d4c6b922cd6284c609cfa6dec0e41615c9c5a1a4ba729a970d8daba05fb" +checksum = "8a7d079e129b77477a49c5c4f1cfe9ce6c2c909ef52520693e8e811a714c7b20" dependencies = [ "Inflector", "pmutil", @@ -462,23 +480,25 @@ checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] -name = "kaze-style-pre-transform-swc-plugin" +name = "kaze_style_pre_transform_swc_plugin" version = "0.1.0" dependencies = [ + "helper", "serde", "serde_json", "swc_core", ] [[package]] -name = "kaze-style-transform-swc-plugin" +name = "kaze_style_transform_swc_plugin" version = "0.1.0" dependencies = [ + "helper", "serde", "serde_json", "swc_core", @@ -565,9 +585,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.138" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "lock_api" @@ -636,9 +656,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.5.4" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" dependencies = [ "adler", ] @@ -692,28 +712,28 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] [[package]] name = "object" -version = "0.29.0" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "output_vt100" @@ -748,9 +768,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.5" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", @@ -876,19 +896,28 @@ dependencies = [ [[package]] name = "proc-macro-hack" -version = "0.5.19" +version = "0.5.20+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + [[package]] name = "ptr_meta" version = "0.1.4" @@ -911,9 +940,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] @@ -959,9 +988,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -985,9 +1014,9 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "relative-path" -version = "1.7.2" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df32d82cedd1499386877b062ebe8721f806de80b08d183c70184ef17dd1d42" +checksum = "d3bf6b372449361333ac1f498b7edae4dd5e70dccd7c0c2a7c7bce8f05ede648" [[package]] name = "remove_dir_all" @@ -1055,15 +1084,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "scoped-tls" @@ -1100,18 +1129,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.149" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.149" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -1120,9 +1149,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.89" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ "itoa", "ryu", @@ -1169,17 +1198,16 @@ checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" [[package]] name = "sourcemap" -version = "6.2.0" +version = "6.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c46fdc1838ff49cf692226f5c2b0f5b7538f556863d0eca602984714667ac6e7" +checksum = "aebe057d110ddba043708da3fb010bf562ff6e9d4d60c9ee92860527bcbeccd6" dependencies = [ "base64", "if_chain", - "lazy_static", - "regex", "rustc_version", "serde", "serde_json", + "unicode-id", "url", ] @@ -1189,6 +1217,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "stacker" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "winapi", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -1270,9 +1311,9 @@ dependencies = [ [[package]] name = "swc_atoms" -version = "0.4.25" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b8033a868fbebf5829797ac0c543499622b657e2d33a08ca6ab12547b8bafc" +checksum = "f88175a66f5a7c189e752bda520e148317776ecb22c75adc2c2f24c490834bd0" dependencies = [ "once_cell", "rkyv", @@ -1285,9 +1326,9 @@ dependencies = [ [[package]] name = "swc_common" -version = "0.29.19" +version = "0.29.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e2328ba5e7c8f83ff8273b352c890f981d80d215ee29cddcbe19aa789d3592" +checksum = "1fc8e0e8109b26be70c82d9709562fc88cbcc09e03c2458221cf216c0088dea2" dependencies = [ "ahash", "anyhow", @@ -1318,9 +1359,9 @@ dependencies = [ [[package]] name = "swc_core" -version = "0.44.6" +version = "0.59.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9593e3d1dca44da09b4601bdf74f2eb5ade8768a31656834036aa5d3754ba9c0" +checksum = "68f5e05fb254be0384197b7d5ba19ad5f73764c754f96d49683ca8c86f9c1283" dependencies = [ "once_cell", "swc_atoms", @@ -1337,9 +1378,9 @@ dependencies = [ [[package]] name = "swc_ecma_ast" -version = "0.95.3" +version = "0.96.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420947496193d5d7f47999ea2d438a3a41e1042393520e28dfb978655f5cacc8" +checksum = "621c66e27fbb6cbb6434a4e2b25e439e9a2583cc3419a4a83eba51d16ac0cd7b" dependencies = [ "bitflags", "is-macro", @@ -1355,9 +1396,9 @@ dependencies = [ [[package]] name = "swc_ecma_codegen" -version = "0.128.5" +version = "0.129.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f63f42f1df360e1228867bfe2cfaeec098b8ba44cc48b122b9eb47041804318" +checksum = "ed38caed505e7e00c58e15c5dd36a1ece62c5e6b19b4efe18833747895a7a052" dependencies = [ "memchr", "num-bigint", @@ -1387,9 +1428,9 @@ dependencies = [ [[package]] name = "swc_ecma_parser" -version = "0.123.5" +version = "0.124.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36e1f25619baa61f14bf19fcdf71b2608ff8e1ddfc3049c568d77be156db147d" +checksum = "89b3f472b3dfbfd279de364d2b014459a281824b938e243a8739037c445d6b6c" dependencies = [ "either", "enum_kind", @@ -1397,6 +1438,7 @@ dependencies = [ "num-bigint", "serde", "smallvec", + "stacker", "swc_atoms", "swc_common", "swc_ecma_ast", @@ -1418,9 +1460,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_base" -version = "0.112.5" +version = "0.116.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d464e86b5273482f934142928def1e2dd9ba8ff586d04977acf28e95ee636700" +checksum = "1607f54ac0a3e677f5a61aa71d47f067630af66ddf8a0f66092139520c4ca53c" dependencies = [ "better_scoped_tls", "bitflags", @@ -1440,9 +1482,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_testing" -version = "0.115.6" +version = "0.119.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b38d353a1b45949b2ce1451d6362fa429e5861cc74935a2728c5c18a82327e" +checksum = "05a1509a523804cf0467b84d5b801939d6305ee2a3506619d235556c3504b1de" dependencies = [ "ansi_term", "anyhow", @@ -1466,13 +1508,14 @@ dependencies = [ [[package]] name = "swc_ecma_utils" -version = "0.106.5" +version = "0.107.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d350bea15d0c71c36a65af37217b32f2675e9d88cd484c11e48beaf9dd2057a" +checksum = "0e4473b78bb2d4ef4f12f9c09dd3756fc108df1a9b64375b585ff3fff80c0832" dependencies = [ "indexmap", "num_cpus", "once_cell", + "rustc-hash", "swc_atoms", "swc_common", "swc_ecma_ast", @@ -1483,9 +1526,9 @@ dependencies = [ [[package]] name = "swc_ecma_visit" -version = "0.81.3" +version = "0.82.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e4b92aa87251452508165d5e86100d35454857cd0c985a9a3bed3dd15a2eb24" +checksum = "f7d44d0d7c3cf08d4553d1e37e34cd2cd56efc28ee90e49fbfa7073a9c5bbfba" dependencies = [ "num-bigint", "swc_atoms", @@ -1509,9 +1552,9 @@ dependencies = [ [[package]] name = "swc_error_reporters" -version = "0.13.20" +version = "0.13.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b8dba54343538503f4e8f8110b569dcf2ac0781b0afd7a950fdc97814f14a4c" +checksum = "831f8acfedd9f6692fa433053959f983e4ee1addb914d2d9fe7dfefd7301c0de" dependencies = [ "anyhow", "miette", @@ -1554,9 +1597,9 @@ dependencies = [ [[package]] name = "swc_plugin_proxy" -version = "0.23.3" +version = "0.25.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e86675e04908eb81ba42376166cb3bf9360b2f11b26d33ebd165f5d62a5d89" +checksum = "cd6df98daa1e20bdfbd2f9d4c2978daa606e53d676f1b2f72d3534cb9ce880ba" dependencies = [ "better_scoped_tls", "rkyv", @@ -1579,9 +1622,9 @@ dependencies = [ [[package]] name = "swc_visit" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82f2bcb7223e185c4c7cbf5e0c1207dec6d2bfd5e72e3fb7b3e8d179747e9130" +checksum = "470a1963cf182fdcbbac46e3a7fd2caf7329da0e568d3668202da9501c880e16" dependencies = [ "either", "swc_visit_macros", @@ -1589,9 +1632,9 @@ dependencies = [ [[package]] name = "swc_visit_macros" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb1f3561674d84947694d41fb6d5737d19539222779baeac1b3a071a2b29428" +checksum = "6098b717cfd4c85f5cddec734af191dbce461c39975ed567c32ac6d0c6d61a6d" dependencies = [ "Inflector", "pmutil", @@ -1603,9 +1646,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.105" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +checksum = "d56e159d99e6c2b93995d171050271edb50ecc5288fbc7cc17de8fdce4e58c14" dependencies = [ "proc-macro2", "quote", @@ -1628,9 +1671,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] @@ -1647,9 +1690,9 @@ dependencies = [ [[package]] name = "testing" -version = "0.31.20" +version = "0.31.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b96c1192fef3c7f6c7962e5861c3c90982ee0cfba5a5fbb1c666ab8df4b495e" +checksum = "dd6790a57e1bddc6cc21b77dfee8fd4559c156ca7cd5a7386cf64b3e2889c3b0" dependencies = [ "ansi_term", "difference", @@ -1694,18 +1737,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -1714,18 +1757,19 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] [[package]] name = "time" -version = "0.3.17" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2" dependencies = [ "itoa", "serde", @@ -1741,9 +1785,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" [[package]] name = "time-macros" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +checksum = "a460aeb8de6dcb0f381e1ee05f1cd56fcf5a5f6eb8187ff3d8f0b11078d38b7c" dependencies = [ "time-core", ] @@ -1759,9 +1803,9 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tracing" @@ -1837,9 +1881,9 @@ dependencies = [ [[package]] name = "typed-arena" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" [[package]] name = "typenum" @@ -1849,9 +1893,9 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-id" @@ -1861,9 +1905,9 @@ checksum = "d70b6494226b36008c8366c288d77190b3fad2eb4c10533139c1c1f461127f1a" [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-linebreak" @@ -1909,9 +1953,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "vergen" -version = "7.4.3" +version = "7.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447f9238a4553957277b3ee09d80babeae0811f1b3baefb093de1c0448437a37" +checksum = "f21b881cd6636ece9735721cf03c1fe1e774fe258683d084bb2812ab67435749" dependencies = [ "anyhow", "cfg-if", @@ -1967,9 +2011,18 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.42.0" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -1982,45 +2035,45 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "yansi" diff --git a/packages/swc-plugin/Cargo.toml b/packages/swc-plugin/Cargo.toml index 052a6726..492dd809 100644 --- a/packages/swc-plugin/Cargo.toml +++ b/packages/swc-plugin/Cargo.toml @@ -1,7 +1,11 @@ [workspace] members = [ - "plugins/*", + "crates/*", ] [profile.release] +# https://swc.rs/docs/plugin/publishing#adjusting-configuration-for-smaller-binary +codegen-units = 1 lto = true +opt-level = "s" +strip = "symbols" diff --git a/packages/swc-plugin/crates/helper/Cargo.toml b/packages/swc-plugin/crates/helper/Cargo.toml new file mode 100644 index 00000000..35196a62 --- /dev/null +++ b/packages/swc-plugin/crates/helper/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "helper" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = "1" +serde_json = "1.0.89" +swc_core = { version = "0.59.*", features = [ "ecma_plugin_transform", "ecma_ast", "common"] } diff --git a/packages/swc-plugin/crates/helper/src/ast_node.rs b/packages/swc-plugin/crates/helper/src/ast_node.rs new file mode 100644 index 00000000..67020672 --- /dev/null +++ b/packages/swc-plugin/crates/helper/src/ast_node.rs @@ -0,0 +1,139 @@ +use serde::{Deserialize, Serialize}; +use swc_core::{ + common::DUMMY_SP, + ecma::ast::{ + ArrayLit, Bool, CallExpr, Callee, Expr, ExprOrSpread, Ident, KeyValueProp, Lit, Null, Number, + ObjectLit, Prop, PropName, PropOrSpread, Str, + }, +}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct StringLiteral { + value: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct NumberLiteral { + value: usize, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct BooleanLiteral { + value: bool, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct NullLiteral {} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Identifier { + name: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ArrayExpression { + elements: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ObjectProperty { + key: String, + value: AstNode, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ObjectExpression { + properties: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct CallExpression { + name: String, + import_source: Option, + arguments: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(tag = "type")] +pub enum AstNode { + String(StringLiteral), + Number(NumberLiteral), + Boolean(BooleanLiteral), + Null(NullLiteral), + Identifier(Identifier), + Array(ArrayExpression), + Object(ObjectExpression), + Call(CallExpression), +} + +pub fn node_to_expr(value: &AstNode) -> Expr { + match value { + AstNode::String(string) => Expr::Lit(Lit::Str(Str { + value: string.value.to_string().into(), + span: DUMMY_SP, + raw: None, + })), + AstNode::Number(number) => Expr::Lit(Lit::Num(Number { + value: number.value as f64, + span: DUMMY_SP, + raw: None, + })), + AstNode::Boolean(boolean) => Expr::Lit(Lit::Bool(Bool { + value: boolean.value.into(), + span: DUMMY_SP, + })), + AstNode::Null(_null) => Expr::Lit(Lit::Null(Null { span: DUMMY_SP })), + AstNode::Identifier(ident) => Expr::Ident(Ident { + optional: false, + span: DUMMY_SP, + sym: ident.name.to_string().into(), + }), + AstNode::Array(array) => Expr::Array(ArrayLit { + span: DUMMY_SP, + elems: array + .elements + .iter() + .map(|node| { + Some(ExprOrSpread { + spread: None, + expr: Box::new(node_to_expr(node)), + }) + }) + .collect::>>(), + }), + AstNode::Object(object) => Expr::Object(ObjectLit { + span: DUMMY_SP, + props: object + .properties + .iter() + .map(|object_property| { + PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp { + key: PropName::Str(Str { + raw: None, + span: DUMMY_SP, + value: object_property.key.to_string().into(), + }), + value: Box::new(node_to_expr(&object_property.value)), + }))) + }) + .collect::>(), + }), + AstNode::Call(call) => Expr::Call(CallExpr { + span: DUMMY_SP, + args: call + .arguments + .iter() + .map(|argument| ExprOrSpread { + spread: None, + expr: Box::new(node_to_expr(argument)), + }) + .collect::>(), + callee: Callee::Expr(Box::new(Expr::Ident(Ident { + optional: false, + span: DUMMY_SP, + sym: call.name.to_string().into(), + }))), + type_args: None, + }), + } +} diff --git a/packages/swc-plugin/crates/helper/src/common_config.rs b/packages/swc-plugin/crates/helper/src/common_config.rs new file mode 100644 index 00000000..38578dec --- /dev/null +++ b/packages/swc-plugin/crates/helper/src/common_config.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct InputTransform { + pub source: String, + pub from: String, + pub to: String, +} diff --git a/packages/swc-plugin/crates/helper/src/lib.rs b/packages/swc-plugin/crates/helper/src/lib.rs new file mode 100644 index 00000000..597e9987 --- /dev/null +++ b/packages/swc-plugin/crates/helper/src/lib.rs @@ -0,0 +1,2 @@ +pub mod ast_node; +pub mod common_config; diff --git a/packages/swc-plugin/crates/pre-transform/Cargo.toml b/packages/swc-plugin/crates/pre-transform/Cargo.toml new file mode 100644 index 00000000..8f5be917 --- /dev/null +++ b/packages/swc-plugin/crates/pre-transform/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "kaze_style_pre_transform_swc_plugin" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +helper = { path = "../helper" } +serde = "1" +serde_json = "1.0.89" +swc_core = { version = "0.59.*", features = [ + "ecma_plugin_transform", + "ecma_visit", + "ecma_ast", + "common", +] } diff --git a/packages/swc-plugin/crates/pre-transform/src/config.rs b/packages/swc-plugin/crates/pre-transform/src/config.rs new file mode 100644 index 00000000..4556911a --- /dev/null +++ b/packages/swc-plugin/crates/pre-transform/src/config.rs @@ -0,0 +1,16 @@ +use serde::{Deserialize, Serialize}; + +use helper::ast_node::AstNode; +use helper::common_config::InputTransform; + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct InputConfig { + pub transforms: Vec, + pub build_arg: AstNode, + pub transformed_comment: String, +} + +pub fn parse_config(json: &str) -> InputConfig { + serde_json::from_str::(&json).expect("Invalid plugin config") +} diff --git a/packages/swc-plugin/crates/pre-transform/src/lib.rs b/packages/swc-plugin/crates/pre-transform/src/lib.rs new file mode 100644 index 00000000..741b4b26 --- /dev/null +++ b/packages/swc-plugin/crates/pre-transform/src/lib.rs @@ -0,0 +1,404 @@ +mod config; +use config::{parse_config, InputConfig}; + +use swc_core::{ + common::{ + comments::{Comment, CommentKind, Comments}, + DUMMY_SP, + }, + ecma::{ + ast::{ + CallExpr, Callee, Expr, ExprOrSpread, Id, Ident, ImportSpecifier, Lit, MemberExpr, + MemberProp, Module, ModuleDecl, ModuleExportName, ModuleItem, Number, Program, + }, + visit::{as_folder, FoldWith, VisitMut, VisitMutWith}, + }, + plugin::{ + plugin_transform, + proxies::{PluginCommentsProxy, TransformPluginProgramMetadata}, + }, +}; + +use helper::ast_node::node_to_expr; + +pub struct Transform { + from: String, + import_source: String, + ids: Vec, + namespace_ids: Vec, +} + +pub struct TransformVisitor { + target_index: usize, + is_use_namespace: bool, + is_transformed: bool, + transformed_comment: String, + comments: PluginCommentsProxy, + transforms: Vec, + build_arg: Expr, +} + +impl TransformVisitor { + fn new(input_config: InputConfig, comments: PluginCommentsProxy) -> Self { + let transforms = input_config + .transforms + .iter() + .map(|input_transform| Transform { + from: input_transform.from.to_string(), + import_source: input_transform.source.to_string(), + ids: vec![], + namespace_ids: vec![], + }) + .collect::>(); + Self { + target_index: 0, + is_use_namespace: false, + is_transformed: false, + comments: comments, + transforms: transforms, + transformed_comment: input_config.transformed_comment, + build_arg: node_to_expr(&input_config.build_arg), + } + } + + fn is_target_call_expr(&self, call_expr: &CallExpr, transform: &Transform) -> bool { + let mut is_target = false; + match &call_expr.callee { + Callee::Expr(callee_expr) => { + let callee_expr = &**callee_expr; + match callee_expr { + Expr::Ident(callee_ident) => { + let callee_ident_is_target = transform + .ids + .iter() + .any(|transform_id| *transform_id == callee_ident.to_id()); + is_target = callee_ident_is_target; + } + Expr::Member(member_expr) => { + match &member_expr.prop { + MemberProp::Ident(member_ident) => { + let member_ident_is_target = + transform + .namespace_ids + .iter() + .any(|namespace_id| match &*member_expr.obj { + Expr::Ident(member_obj_ident) => transform.ids.iter().any(|transform_id| { + if *namespace_id == member_obj_ident.to_id() + && *transform_id == member_ident.to_id() + { + true + } else { + false + } + }), + _ => false, + }); + is_target = member_ident_is_target; + } + _ => {} + }; + } + _ => {} + }; + } + _ => {} + } + return is_target.clone(); + } + + fn set_call_expr_args(&mut self, call_expr: &mut CallExpr) { + for transform in self.transforms.iter() { + let is_target = self.is_target_call_expr(call_expr, transform); + if is_target == true { + call_expr.args.push(ExprOrSpread { + spread: None, + expr: Box::new(self.build_arg.clone()), + }); + call_expr.args.push(ExprOrSpread { + expr: Box::new(Expr::Lit(Lit::Num(Number::from(self.target_index as f64)))), + spread: None, + }); + self.target_index += 1; + if self.is_transformed == false { + self.is_transformed = true; + } + } + } + } + + fn target_namespace_import_extract_id(&mut self, member_expr: &mut MemberExpr) { + if self.is_use_namespace == true { + for transform in self.transforms.iter_mut() { + for namespace_id in transform.namespace_ids.iter() { + match &*member_expr.obj { + Expr::Ident(expr_ident) => { + if *namespace_id == expr_ident.to_id() { + match &mut member_expr.prop { + MemberProp::Ident(prop_ident) => { + if prop_ident.sym == transform.from { + transform.ids.push(prop_ident.to_id()); + } + } + _ => {} + } + } + } + _ => {} + } + } + } + } + } + + fn target_imports_extract_id(&mut self, module: &mut Module) { + for module_item in module.body.iter_mut() { + match module_item { + ModuleItem::ModuleDecl(ModuleDecl::Import(import_decl)) => { + if import_decl.type_only { + return; + } + for transform in self.transforms.iter_mut() { + if &*import_decl.src.value == transform.import_source { + for import_specifier in import_decl.specifiers.iter_mut() { + match import_specifier { + ImportSpecifier::Named(import_named) => { + let import_ident: Option<&Ident> = match &import_named.imported { + Some(import_named_imported) => { + match import_named_imported { + // import { named as _named } from '' + ModuleExportName::Ident(imported_ident) => Some(imported_ident), + // import { "named" as _named } from '' ??? + ModuleExportName::Str(_) => None, + } + } + // import { named } from '' + None => Some(&import_named.local), + }; + match import_ident { + Some(import_ident) => { + if import_ident.sym.to_string() == transform.from { + transform.ids.push(import_named.local.to_id()); + } + } + None => {} + }; + } + // import default from '' + ImportSpecifier::Default(_) => {} + // import * as namespace from '' + ImportSpecifier::Namespace(namespace) => { + transform.namespace_ids.push(namespace.local.to_id()); + if self.is_use_namespace == false { + self.is_use_namespace = true; + } + } + } + } + } + } + } + _ => {} + } + } + } + + fn add_transformed_comment(&mut self, module: &mut Module) { + if self.is_transformed == true { + self.comments.add_leading( + module.span.lo, + Comment { + kind: CommentKind::Block, + span: DUMMY_SP, + text: self.transformed_comment.clone().into(), + }, + ); + } + } +} + +impl VisitMut for TransformVisitor { + fn visit_mut_member_expr(&mut self, member_expr: &mut MemberExpr) { + self.target_namespace_import_extract_id(member_expr); + member_expr.visit_mut_children_with(self); + } + + fn visit_mut_call_expr(&mut self, call_expr: &mut CallExpr) { + call_expr.visit_mut_children_with(self); + self.set_call_expr_args(call_expr); + } + + fn visit_mut_module(&mut self, module: &mut Module) { + self.target_imports_extract_id(module); + module.visit_mut_children_with(self); + self.add_transformed_comment(module); + } +} + +#[plugin_transform] +pub fn process_transform(program: Program, metadata: TransformPluginProgramMetadata) -> Program { + let config = parse_config(&metadata.get_transform_plugin_config().expect("")); + program.fold_with(&mut as_folder(TransformVisitor::new( + config, + PluginCommentsProxy, + ))) +} + +#[cfg(test)] +mod tests { + use super::config::{parse_config, InputConfig}; + use crate::TransformVisitor; + use serde_json::json; + use swc_core::{ + ecma::{transforms::testing::test, visit::as_folder}, + plugin::proxies::PluginCommentsProxy, + }; + + fn get_config() -> InputConfig { + let config_json = json!({ + "transforms": [ + { + "source": "target_source", + "from": "target", + "to": "__target" + }, + ], + "buildArg": { + "type": "Object", + "properties": [ + { + "key": "filename", + "value": { + "type": "String", + "value": "filename.ts" + }, + }, + { + "key": "injector", + "value": { + "type": "Identifier", + "name": "injector" + }, + } + ], + }, + "transformedComment": "transformedComment" + }) + .to_string(); + parse_config(&config_json) + } + test!( + Default::default(), + |_| as_folder(TransformVisitor::new(get_config(), PluginCommentsProxy)), + basic, + // Input codes + r#" + import { target } from 'target_source'; + const x0 = target({}); + target({}); + "#, + // Output codes after + r#" + import { target } from 'target_source'; + const x0 = target({}, { "filename": "filename.ts", "injector": injector }, 0); + target({}, { "filename": "filename.ts", "injector": injector }, 1); + "# + ); + + test!( + Default::default(), + |_| as_folder(TransformVisitor::new(get_config(), PluginCommentsProxy)), + as_basic, + // Input codes + r#" + import { target as _target1 } from 'target_source'; + import { target as _target2 } from 'target_source'; + const x0 = _target1({}); + const x1 = _target2({}); + "#, + // Output codes after + r#" + import { target as _target1 } from 'target_source'; + import { target as _target2 } from 'target_source'; + const x0 = _target1({}, { "filename": "filename.ts", "injector": injector }, 0); + const x1 = _target2({}, { "filename": "filename.ts", "injector": injector }, 1); + "# + ); + + test!( + Default::default(), + |_| as_folder(TransformVisitor::new(get_config(), PluginCommentsProxy)), + namespace, + // Input codes + r#" + import * as namespace0 from 'target_source'; + import * as namespace1 from 'target_source'; + const x0 = namespace0.target({}); + const x1 = namespace1.target({}); + "#, + // Output codes after + r#" + import * as namespace0 from 'target_source'; + import * as namespace1 from 'target_source'; + const x0 = namespace0.target({}, { "filename": "filename.ts", "injector": injector }, 0); + const x1 = namespace1.target({}, { "filename": "filename.ts", "injector": injector }, 1); + "# + ); + test!( + Default::default(), + |_| as_folder(TransformVisitor::new(get_config(), PluginCommentsProxy)), + mix, + // Input codes + r#" + import { target, target as _target } from 'target_source'; + import { target as __target } from 'target_source'; + import * as namespace from 'target_source'; + const x0 = target({}); + const x1 = _target({}); + const x2 = __target({}); + const x3 = namespace.target({}); + namespace.target({}); + const x4 = namespace.dummy({}); + "#, + // Output codes after + r#" + import { target, target as _target } from 'target_source'; + import { target as __target } from 'target_source'; + import * as namespace from 'target_source'; + const x0 = target({}, { "filename": "filename.ts", "injector": injector }, 0); + const x1 = _target({}, { "filename": "filename.ts", "injector": injector }, 1); + const x2 = __target({}, { "filename": "filename.ts", "injector": injector }, 2); + const x3 = namespace.target({}, { "filename": "filename.ts", "injector": injector }, 3); + namespace.target({}, { "filename": "filename.ts", "injector": injector }, 4); + const x4 = namespace.dummy({}); + "# + ); + test!( + Default::default(), + |_| as_folder(TransformVisitor::new(get_config(), PluginCommentsProxy)), + last_import_mix, + // Input codes + r#" + const x0 = target({}); + const x1 = _target({}); + const x2 = __target({}); + const x3 = namespace.target({}); + namespace.target({}); + const x4 = namespace.dummy({}); + import { target, target as _target } from 'target_source'; + import { target as __target } from 'target_source'; + import * as namespace from 'target_source'; + "#, + // Output codes after + r#" + const x0 = target({}, { "filename": "filename.ts", "injector": injector }, 0); + const x1 = _target({}, { "filename": "filename.ts", "injector": injector }, 1); + const x2 = __target({}, { "filename": "filename.ts", "injector": injector }, 2); + const x3 = namespace.target({}, { "filename": "filename.ts", "injector": injector }, 3); + namespace.target({}, { "filename": "filename.ts", "injector": injector }, 4); + const x4 = namespace.dummy({}); + import { target, target as _target } from 'target_source'; + import { target as __target } from 'target_source'; + import * as namespace from 'target_source'; + "# + ); +} diff --git a/packages/swc-plugin/crates/transform/Cargo.toml b/packages/swc-plugin/crates/transform/Cargo.toml new file mode 100644 index 00000000..4abc0e74 --- /dev/null +++ b/packages/swc-plugin/crates/transform/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "kaze_style_transform_swc_plugin" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +helper = { path = "../helper" } +serde = "1" +serde_json = "1.0.89" +swc_core = { version = "0.59.*", features = [ + "ecma_plugin_transform", + "ecma_visit", + "ecma_ast", + "common", +] } diff --git a/packages/swc-plugin/crates/transform/src/config.rs b/packages/swc-plugin/crates/transform/src/config.rs new file mode 100644 index 00000000..2ebc76b0 --- /dev/null +++ b/packages/swc-plugin/crates/transform/src/config.rs @@ -0,0 +1,29 @@ +use serde::{Deserialize, Serialize}; + +use helper::ast_node::AstNode; +use helper::common_config::InputTransform; + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct InputConfig { + pub transforms: Vec, + pub inject_args: Vec, + pub imports: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct InputImport { + pub source: String, + pub specifier: String, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct InputArgument { + pub value: Vec, + pub index: usize, +} + +pub fn parse_config(json: &str) -> InputConfig { + serde_json::from_str::(&json).expect("Invalid plugin config") +} diff --git a/packages/swc-plugin/crates/transform/src/lib.rs b/packages/swc-plugin/crates/transform/src/lib.rs new file mode 100644 index 00000000..62dc1710 --- /dev/null +++ b/packages/swc-plugin/crates/transform/src/lib.rs @@ -0,0 +1,497 @@ +mod config; +use config::{parse_config, InputConfig, InputImport}; + +use swc_core::{ + common::DUMMY_SP, + ecma::{ + ast::{ + CallExpr, Callee, Expr, ExprOrSpread, Id, Ident, ImportDecl, ImportNamedSpecifier, + ImportSpecifier, Lit, MemberExpr, MemberProp, Module, ModuleDecl, ModuleExportName, + ModuleItem, Program, Str, + }, + visit::{as_folder, FoldWith, VisitMut, VisitMutWith}, + }, + plugin::{plugin_transform, proxies::TransformPluginProgramMetadata}, +}; + +use helper::ast_node::node_to_expr; + +pub struct Transform { + from: String, + to: String, + import_source: String, + ids: Vec, + namespace_ids: Vec, +} + +pub struct TransformVisitor { + is_use_namespace: bool, + transforms: Vec, + input_imports: Vec, + inject_args: Vec, +} +pub struct ArgExpr { + value: Vec, + index: usize, +} + +impl TransformVisitor { + fn new(input_config: InputConfig) -> Self { + let transforms = input_config + .transforms + .iter() + .map(|input_transform| Transform { + from: input_transform.from.to_string(), + to: input_transform.to.to_string(), + import_source: input_transform.source.to_string(), + ids: vec![], + namespace_ids: vec![], + }) + .collect::>(); + let inject_args = input_config + .inject_args + .iter() + .map(|inject_arg| ArgExpr { + index: inject_arg.index, + value: inject_arg + .value + .iter() + .map(|arg| ExprOrSpread { + expr: Box::new(node_to_expr(arg)), + spread: None, + }) + .collect::>(), + }) + .collect::>(); + Self { + is_use_namespace: false, + transforms: transforms, + input_imports: input_config.imports, + inject_args: inject_args, + } + } + + fn is_target_call_expr(&self, call_expr: &CallExpr, transform: &Transform) -> bool { + let mut is_target = false; + match &call_expr.callee { + Callee::Expr(callee_expr) => { + let callee_expr = &**callee_expr; + match callee_expr { + Expr::Ident(callee_ident) => { + let callee_ident_is_target = transform + .ids + .iter() + .any(|transform_id| *transform_id == callee_ident.to_id()); + is_target = callee_ident_is_target; + } + Expr::Member(member_expr) => { + match &member_expr.prop { + MemberProp::Ident(member_ident) => { + let member_ident_is_target = + transform + .namespace_ids + .iter() + .any(|namespace_id| match &*member_expr.obj { + Expr::Ident(member_obj_ident) => transform.ids.iter().any(|transform_id| { + if *namespace_id == member_obj_ident.to_id() + && *transform_id == member_ident.to_id() + { + true + } else { + false + } + }), + _ => false, + }); + is_target = member_ident_is_target; + } + _ => {} + }; + } + _ => {} + }; + } + _ => {} + } + return is_target.clone(); + } + + fn transform_call_expr_args(&mut self, call_expr: &mut CallExpr) { + for transform in self.transforms.iter() { + let is_target = self.is_target_call_expr(call_expr, transform); + if is_target == true { + let last_arg = call_expr.args.last(); + match last_arg { + Some(last_arg) => { + let arg_index = self.get_index_number(&*last_arg.expr); + match arg_index { + Some(arg_index) => { + let mut is_transform = false; + for inject_arg in self.inject_args.iter() { + if (inject_arg.index as f64) == arg_index { + call_expr.args = inject_arg.value.clone(); + is_transform = true; + break; + } + } + if is_transform == false { + call_expr.args = vec![]; + } + } + None => { + call_expr.args = vec![]; + } + } + } + None => { + call_expr.args = vec![]; + } + } + } + } + } + + fn get_index_number(&self, expr: &Expr) -> Option { + if let Expr::Lit(Lit::Num(index)) = &expr { + Some(index.value) + } else { + None + } + } + + fn transform_namespace_import_ident(&mut self, member_expr: &mut MemberExpr) { + if self.is_use_namespace == true { + for transform in self.transforms.iter_mut() { + for namespace_id in transform.namespace_ids.iter() { + match &*member_expr.obj { + Expr::Ident(expr_ident) => { + if *namespace_id == expr_ident.to_id() { + match &mut member_expr.prop { + MemberProp::Ident(prop_ident) => { + if prop_ident.sym == transform.from { + prop_ident.sym = (&transform.to as &str).into(); + transform.ids.push(prop_ident.to_id()); + } + } + _ => {} + } + } + } + _ => {} + } + } + } + } + } + + fn transform_imports(&mut self, module: &mut Module) { + for module_item in module.body.iter_mut() { + match module_item { + ModuleItem::ModuleDecl(ModuleDecl::Import(import_decl)) => { + if import_decl.type_only { + return; + } + for transform in self.transforms.iter_mut() { + if &*import_decl.src.value == transform.import_source { + for import_specifier in import_decl.specifiers.iter_mut() { + match import_specifier { + ImportSpecifier::Named(import_named) => { + let import_ident: Option<&Ident> = match &import_named.imported { + Some(import_named_imported) => { + match import_named_imported { + // import { named as _named } from '' + ModuleExportName::Ident(imported_ident) => Some(imported_ident), + // import { "named" as _named } from '' ??? + ModuleExportName::Str(_) => None, + } + } + // import { named } from '' + None => Some(&import_named.local), + }; + match import_ident { + Some(import_ident) => { + if import_ident.sym.to_string() == transform.from { + transform.ids.push(import_named.local.to_id()); + import_named.imported = Some(ModuleExportName::Ident(Ident { + optional: false, + span: DUMMY_SP, + sym: (&transform.to as &str).into(), + })); + } + } + None => {} + }; + } + // import default from '' + ImportSpecifier::Default(_) => {} + // import * as namespace from '' + ImportSpecifier::Namespace(namespace) => { + transform.namespace_ids.push(namespace.local.to_id()); + if self.is_use_namespace == false { + self.is_use_namespace = true; + } + } + } + } + } + } + } + _ => {} + } + } + } + + fn add_imports(&mut self, module: &mut Module) { + for import in self.input_imports.iter() { + module.body.insert( + 0, + ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl { + span: DUMMY_SP, + asserts: None, + type_only: false, + src: Box::new(Str { + span: DUMMY_SP, + raw: None, + value: import.source.clone().into(), + }), + specifiers: vec![ImportSpecifier::Named(ImportNamedSpecifier { + span: DUMMY_SP, + imported: None, + is_type_only: false, + local: Ident { + span: DUMMY_SP, + optional: false, + sym: import.specifier.clone().into(), + }, + })], + })), + ); + } + } +} + +impl VisitMut for TransformVisitor { + fn visit_mut_member_expr(&mut self, member_expr: &mut MemberExpr) { + self.transform_namespace_import_ident(member_expr); + member_expr.visit_mut_children_with(self); + } + + fn visit_mut_call_expr(&mut self, call_expr: &mut CallExpr) { + call_expr.visit_mut_children_with(self); + self.transform_call_expr_args(call_expr); + } + + fn visit_mut_module(&mut self, module: &mut Module) { + self.transform_imports(module); + self.add_imports(module); + module.visit_mut_children_with(self); + } +} + +#[plugin_transform] +pub fn process_transform(program: Program, metadata: TransformPluginProgramMetadata) -> Program { + let config = parse_config(&metadata.get_transform_plugin_config().expect("")); + program.fold_with(&mut as_folder(TransformVisitor::new(config))) +} + +#[cfg(test)] +mod tests { + use super::config::{parse_config, InputConfig}; + use crate::TransformVisitor; + use serde_json::json; + use swc_core::ecma::{transforms::testing::test, visit::as_folder}; + + fn get_config() -> InputConfig { + let config_json = json!({ + "transforms": [ + { + "source": "target_source", + "from": "target", + "to": "__target" + }, + ], + "injectArgs": [ + { + "value": [ + { + "type": "Object", + "properties": [ + { + "key": "color", + "value": { + "type": "String", + "value": "red0" + }, + }, + ], + } + ], + "index": 0 + }, + { + "value": [ + { + "type": "Object", + "properties": [ + { + "key": "color", + "value": { + "type": "String", + "value": "red1" + }, + }, + ], + } + ], + "index": 1 + }, + { + "value": [ + { + "type": "Object", + "properties": [ + { + "key": "color", + "value": { + "type": "String", + "value": "red3" + }, + }, + ], + } + ], + "index": 3 + }, + { + "value": [ + { + "type": "Object", + "properties": [ + { + "key": "color", + "value": { + "type": "String", + "value": "red5" + }, + }, + ], + } + ], + "index": 5 + } + ], + "imports": [] + }) + .to_string(); + parse_config(&config_json) + } + test!( + Default::default(), + |_| as_folder(TransformVisitor::new(get_config())), + basic, + // Input codes + r#" + import { target } from 'target_source'; + const x0 = target({},0); + "#, + // Output codes after + r#" + import { __target as target } from 'target_source'; + const x0 = target({"color" : "red0" }); + "# + ); + + test!( + Default::default(), + |_| as_folder(TransformVisitor::new(get_config())), + as_basic, + // Input codes + r#" + import { target as _target1 } from 'target_source'; + import { target as _target2 } from 'target_source'; + const x0 = _target1({},0); + const x1 = _target2({},1); + "#, + // Output codes after + r#" + import { __target as _target1 } from 'target_source'; + import { __target as _target2 } from 'target_source'; + const x0 = _target1({ "color": "red0" }); + const x1 = _target2({ "color": "red1" }); + "# + ); + + test!( + Default::default(), + |_| as_folder(TransformVisitor::new(get_config())), + namespace, + // Input codes + r#" + import * as namespace1 from 'target_source'; + import * as namespace2 from 'target_source'; + const x0 = namespace1.target({}, 0); + const x1 = namespace2.target({}, 1); + "#, + // Output codes after + r#" + import * as namespace1 from 'target_source'; + import * as namespace2 from 'target_source'; + const x0 = namespace1.__target({ "color" : "red0" }); + const x1 = namespace2.__target({ "color" : "red1" }); + "# + ); + test!( + Default::default(), + |_| as_folder(TransformVisitor::new(get_config())), + mix, + // Input codes + r#" + import { target, target as _target } from 'target_source'; + import { target as __target } from 'target_source'; + import * as namespace from 'target_source'; + const x0 = target({}, 0); + const x1 = _target({}, {}, 1); + const x2 = __target({}, {}, 2); + const x3 = namespace.target({}, 3); + const x4 = namespace.dummy({}, {}, 4); + "#, + // Output codes after + r#" + import { __target as target, __target as _target } from 'target_source'; + import { __target as __target } from 'target_source'; + import * as namespace from 'target_source'; + const x0 = target({ "color": "red0" }); + const x1 = _target({ "color": "red1" }); + const x2 = __target(); + const x3 = namespace.__target({ "color": "red3" }); + const x4 = namespace.dummy({}, {}, 4); + "# + ); + test!( + Default::default(), + |_| as_folder(TransformVisitor::new(get_config())), + last_import_mix, + // Input codes + r#" + const x0 = target({}, 0); + const x1 = _target({}, {}, 1); + const x2 = __target({}, {}, 2); + const x3 = namespace.target({}, 3); + const x4 = namespace.dummy({}, {}, 4); + import { target, target as _target } from 'target_source'; + import { target as __target } from 'target_source'; + import * as namespace from 'target_source'; + "#, + // Output codes after + r#" + const x0 = target({ "color": "red0" }); + const x1 = _target({ "color": "red1" }); + const x2 = __target(); + const x3 = namespace.__target({ "color": "red3" }); + const x4 = namespace.dummy({}, {}, 4); + import { __target as target, __target as _target } from 'target_source'; + import { __target as __target } from 'target_source'; + import * as namespace from 'target_source'; + "# + ); +} diff --git a/packages/swc-plugin/package.json b/packages/swc-plugin/package.json index 299d829d..e4ee649f 100644 --- a/packages/swc-plugin/package.json +++ b/packages/swc-plugin/package.json @@ -30,6 +30,7 @@ ], "scripts": { "test": "cargo test", + "test:log": "cargo test -- --nocapture", "code-fix": "cargo fmt", "code-check": "cargo fmt --check", "type-check": "tsc --noEmit", diff --git a/packages/swc-plugin/plugins/pre-transform/Cargo.toml b/packages/swc-plugin/plugins/pre-transform/Cargo.toml deleted file mode 100644 index 1a128451..00000000 --- a/packages/swc-plugin/plugins/pre-transform/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "kaze-style-pre-transform-swc-plugin" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["cdylib"] - -[dependencies] -serde = "1" -serde_json = "1.0.89" -swc_core = { version = "0.44.*", features = ["plugin_transform"] } diff --git a/packages/swc-plugin/plugins/pre-transform/src/lib.rs b/packages/swc-plugin/plugins/pre-transform/src/lib.rs deleted file mode 100644 index 71d012ef..00000000 --- a/packages/swc-plugin/plugins/pre-transform/src/lib.rs +++ /dev/null @@ -1,204 +0,0 @@ -use swc_core::{ - common::{ - comments::{Comment, CommentKind, Comments}, - DUMMY_SP, - }, - ecma::{ - ast::{ - CallExpr, Callee, Expr, ExprOrSpread, Id, Ident, ImportDecl, ImportSpecifier, Lit, Module, - Number, Program, Str, - }, - transforms::testing::test, - visit::{as_folder, FoldWith, VisitMut, VisitMutWith}, - }, - plugin::{ - plugin_transform, - proxies::{PluginCommentsProxy, TransformPluginProgramMetadata}, - }, -}; - -#[derive(serde::Deserialize)] -pub struct Config { - filename: String, - #[serde(rename = "forBuildName")] - for_build_name: String, -} - -pub fn json_to_config(json: Option) -> Config { - let config: Result = if let Some(config) = json { - serde_json::from_str(&config) - } else { - panic!("config parse Error") - }; - - let config = match config { - Ok(config) => config, - Err(_) => panic!("config parse Error"), - }; - config -} - -pub struct Transform { - from: String, - to: String, - import_id: Option, -} - -pub struct TransformVisitor { - config: Config, - comments: PluginCommentsProxy, - index: u8, - import_source: String, - transformed_comment: String, - is_transformed: bool, - transforms: Vec, -} - -impl TransformVisitor { - fn new(config: Config, comments: PluginCommentsProxy) -> Self { - Self { - config: config, - comments: comments, - index: 0, - import_source: "@kaze-style/core".to_string(), - transformed_comment: "__kaze-style-pre-transformed".to_string(), - is_transformed: false, - transforms: vec![ - Transform { - from: "style".to_string(), - to: "__preStyle".to_string(), - import_id: None, - }, - Transform { - from: "globalStyle".to_string(), - to: "__preGlobalStyle".to_string(), - import_id: None, - }, - ], - } - } - - fn transform_target_import(&mut self, import_decl: &mut ImportDecl) { - if &*import_decl.src.value == self.import_source { - for specifier in import_decl.specifiers.iter_mut() { - match specifier { - ImportSpecifier::Named(named_specifier) => { - for transform in self.transforms.iter_mut() { - if &transform.from == &*named_specifier.local.sym { - let transform_to: &str = &transform.to; - transform.import_id = Some(named_specifier.local.to_id()); - named_specifier.local.sym = transform_to.into(); - } - } - } - _ => {} - } - } - } - } - - fn transform_target_call_ident(&mut self, call_expr: &mut CallExpr) { - if let Callee::Expr(expr) = &mut call_expr.callee { - if let Expr::Ident(ident) = &mut **expr { - for transform in self.transforms.iter_mut() { - if let Some(imported_id) = &transform.import_id { - if &*ident.sym == &transform.from - && *imported_id == ident.to_id() - && call_expr.args.len() == 1 - { - let transform_to: &str = &transform.to; - let filename: &str = &self.config.filename; - let index: f64 = self.index.into(); - let for_build_name: &str = &self.config.for_build_name; - let for_build_name = Ident { - span: DUMMY_SP, - optional: false, - sym: for_build_name.into(), - }; - ident.sym = transform_to.into(); - call_expr.args.push(ExprOrSpread { - expr: Box::new(Expr::Ident(for_build_name)), - spread: None, - }); - call_expr.args.push(ExprOrSpread { - expr: Box::new(Expr::Lit(Lit::Str(Str::from(filename)))), - spread: None, - }); - call_expr.args.push(ExprOrSpread { - expr: Box::new(Expr::Lit(Lit::Num(Number::from(index)))), - spread: None, - }); - self.index += 1; - if self.is_transformed == false { - self.is_transformed = true; - } - } - } - } - } - } - } - - fn add_comment(&mut self, module: &mut Module) { - if self.is_transformed { - let transformed_comment: &str = &self.transformed_comment; - self.comments.add_leading( - module.span.lo, - Comment { - kind: CommentKind::Block, - span: DUMMY_SP, - text: transformed_comment.into(), - }, - ); - } - } -} - -impl VisitMut for TransformVisitor { - fn visit_mut_import_decl(&mut self, import_decl: &mut ImportDecl) { - self.transform_target_import(import_decl); - import_decl.visit_mut_children_with(self); - } - - fn visit_mut_call_expr(&mut self, call_expr: &mut CallExpr) { - self.transform_target_call_ident(call_expr); - call_expr.visit_mut_children_with(self); - } - fn visit_mut_module(&mut self, module: &mut Module) { - module.visit_mut_children_with(self); - self.add_comment(module); - } -} - -#[plugin_transform] -pub fn process_transform(program: Program, metadata: TransformPluginProgramMetadata) -> Program { - let config = json_to_config(metadata.get_transform_plugin_config()); - program.fold_with(&mut as_folder(TransformVisitor::new( - config, - PluginCommentsProxy, - ))) -} - -test!( - Default::default(), - |_| as_folder(TransformVisitor::new( - Config { - filename: "filename.ts".to_string(), - for_build_name: "for_build_name".to_string() - }, - PluginCommentsProxy - )), - test, - // Input codes - r#" - import { style, globalStyle, mergeStyle } from '@kaze-style/core'; - const c = style({}); - globalStyle({}); - "#, - // Output codes after - r#" - import { __preStyle, __preGlobalStyle, mergeStyle } from '@kaze-style/core'; - const c = __preStyle({}, for_build_name, "filename.ts", 0); - __preGlobalStyle({}, for_build_name, "filename.ts", 1); - "# -); diff --git a/packages/swc-plugin/plugins/transform/Cargo.toml b/packages/swc-plugin/plugins/transform/Cargo.toml deleted file mode 100644 index c41603e2..00000000 --- a/packages/swc-plugin/plugins/transform/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "kaze-style-transform-swc-plugin" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["cdylib"] - -[dependencies] -serde = "1" -serde_json = "1.0.89" -swc_core = { version = "0.44.*", features = ["plugin_transform"] } diff --git a/packages/swc-plugin/plugins/transform/src/lib.rs b/packages/swc-plugin/plugins/transform/src/lib.rs deleted file mode 100644 index 443103af..00000000 --- a/packages/swc-plugin/plugins/transform/src/lib.rs +++ /dev/null @@ -1,270 +0,0 @@ -use std::collections::BTreeMap; -use swc_core::{ - common::DUMMY_SP, - ecma::{ - ast::{ - CallExpr, Callee, Expr, ExprOrSpread, Id, Ident, ImportDecl, ImportNamedSpecifier, - ImportSpecifier, KeyValueProp, Lit, NewExpr, ObjectLit, Program, Prop, PropName, - PropOrSpread, Str, - }, - transforms::testing::test, - visit::{as_folder, FoldWith, VisitMut, VisitMutWith}, - }, - plugin::{plugin_transform, proxies::TransformPluginProgramMetadata}, -}; - -#[derive(serde::Deserialize)] -pub struct Style { - index: u8, - classes: Option>, -} - -#[derive(serde::Deserialize)] -#[serde(untagged)] -enum ClassName { - Str(String), - Obj(BTreeMap), -} - -#[derive(serde::Deserialize)] -pub struct Config { - styles: Vec