From 01aafff28858a91a3be27769264d5e34285e2780 Mon Sep 17 00:00:00 2001 From: ClarkXia Date: Mon, 12 Jul 2021 16:33:37 +0800 Subject: [PATCH 1/3] feat: build-plugin-fusion-legacy --- packages/plugin-fusion-legacy/CHANGELOG.md | 5 + packages/plugin-fusion-legacy/package.json | 15 ++ packages/plugin-fusion-legacy/src/index.ts | 223 ++++++++++++++++++ .../src/postcssPluginPx2vw.ts} | 47 ++-- .../src/theme/colorNames.ts} | 6 +- .../src/theme/getCalcVars.ts} | 15 +- .../src/theme/getThemeCode.ts} | 2 +- .../src/theme/getThemeVars.ts} | 7 +- packages/plugin-fusion-legacy/tsconfig.json | 8 + packages/plugin-fusion/package.json | 4 + .../convertCharStr.js => convertCharStr.ts} | 4 +- .../plugin-fusion/src/{index.js => index.ts} | 28 ++- .../{unicodeLoader.js => unicodeLoader.ts} | 4 +- ...kPlugin.js => appendStyleWebpackPlugin.ts} | 40 ++-- .../bizComponentsVersion.js | 104 -------- .../deprecatedComponents.js | 3 - .../checkIceComponentsDepPlugin/index.js | 136 ----------- 17 files changed, 344 insertions(+), 307 deletions(-) create mode 100644 packages/plugin-fusion-legacy/CHANGELOG.md create mode 100644 packages/plugin-fusion-legacy/package.json create mode 100644 packages/plugin-fusion-legacy/src/index.ts rename packages/{plugin-fusion/src/postcssPlugins/postcssPluginPx2vw.js => plugin-fusion-legacy/src/postcssPluginPx2vw.ts} (66%) rename packages/{plugin-fusion/src/utils/colorNames.js => plugin-fusion-legacy/src/theme/colorNames.ts} (98%) rename packages/{plugin-fusion/src/utils/getCalcVars.js => plugin-fusion-legacy/src/theme/getCalcVars.ts} (84%) rename packages/{plugin-fusion/src/utils/getThemeCode.js => plugin-fusion-legacy/src/theme/getThemeCode.ts} (91%) rename packages/{plugin-fusion/src/utils/getThemeVars.js => plugin-fusion-legacy/src/theme/getThemeVars.ts} (91%) create mode 100644 packages/plugin-fusion-legacy/tsconfig.json rename packages/plugin-fusion/src/{utils/convertCharStr.js => convertCharStr.ts} (61%) rename packages/plugin-fusion/src/{index.js => index.ts} (96%) rename packages/plugin-fusion/src/webpackLoaders/{unicodeLoader.js => unicodeLoader.ts} (60%) rename packages/plugin-fusion/src/webpackPlugins/{appendStyleWebpackPlugin.js => appendStyleWebpackPlugin.ts} (85%) delete mode 100644 packages/plugin-fusion/src/webpackPlugins/checkIceComponentsDepPlugin/bizComponentsVersion.js delete mode 100644 packages/plugin-fusion/src/webpackPlugins/checkIceComponentsDepPlugin/deprecatedComponents.js delete mode 100644 packages/plugin-fusion/src/webpackPlugins/checkIceComponentsDepPlugin/index.js diff --git a/packages/plugin-fusion-legacy/CHANGELOG.md b/packages/plugin-fusion-legacy/CHANGELOG.md new file mode 100644 index 0000000000..b66814e5f2 --- /dev/null +++ b/packages/plugin-fusion-legacy/CHANGELOG.md @@ -0,0 +1,5 @@ +# changelog + +## 1.0.0 + +- [feat] init plugin \ No newline at end of file diff --git a/packages/plugin-fusion-legacy/package.json b/packages/plugin-fusion-legacy/package.json new file mode 100644 index 0000000000..7a32faa74d --- /dev/null +++ b/packages/plugin-fusion-legacy/package.json @@ -0,0 +1,15 @@ +{ + "name": "build-plugin-fusion-legacy", + "version": "1.0.0", + "description": "plugin for fusion component to solve legacy config", + "main": "lib/index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "fusion", + "build-scripts" + ], + "author": "", + "license": "MIT" +} diff --git a/packages/plugin-fusion-legacy/src/index.ts b/packages/plugin-fusion-legacy/src/index.ts new file mode 100644 index 0000000000..0fdf3001ea --- /dev/null +++ b/packages/plugin-fusion-legacy/src/index.ts @@ -0,0 +1,223 @@ +import * as path from 'path'; +import * as fs from 'fs'; +import { IPlugin } from 'build-scripts'; +import postcssPluginPx2vw from './postcssPluginPx2vw'; +import getCalcVars from './theme/getCalcVars'; +import getThemeVars from './theme/getThemeVars'; +import getThemeCode from './theme/getThemeCode'; + +interface ThemeItem { + name: string; + default: boolean; + themeConfig: Record; +} + +interface ThemeVars { + scssVars?: Record; + originTheme?: Record; + cssVars?: Record; +} + +interface PluginOptions { + uniteBaseComponent: boolean | string; + usePx2Vw: boolean; + px2vwOptions: object; + cssVariable: boolean; + enableColorNames: boolean; + themePackages: Partial[]; +} + +type GetVariablesPath = (options: { packageName: string; filename?: string; silent?: boolean }) => string; + +const getVariablesPath: GetVariablesPath = (packageName, filename = 'variables.scss', silent = false) => { + let filePath = ''; + const variables = `${packageName}/${filename}`; + try { + filePath = require.resolve(variables); + } catch (err) { + if (!silent) { + console.log('[ERROR]', `fail to resolve ${variables}`); + } + } + return filePath; +}; + +function replaceBlank(str: string) { + return str.replace(/[\r\n ]/g, '').replace(/\\/g, '\\\\'); +} + +function addCSSVariableCode({ rootDir, themesCssVars, defaultTheme, cssVariable, config, log }) { + try { + const tempDir = path.join(rootDir, './node_modules'); + const jsPath = path.join(tempDir, 'change-theme.js'); + fs.writeFileSync(jsPath, getThemeCode(themesCssVars, defaultTheme, cssVariable)); + + // add theme.js to entry + const entryNames = Object.keys(config.entryPoints.entries()); + entryNames.forEach((name) => { + config.entry(name).prepend(jsPath); + }); + } catch (err) { + log.error('fail to add theme.js to entry'); + log.error(err); + } +} + +const plugin: IPlugin = ({ onGetWebpackConfig, log, context }, options) => { + const { + uniteBaseComponent, + usePx2Vw, + px2vwOptions, + themePackages, + cssVariable, + enableColorNames, + } = (options || {}) as Partial; + const { rootDir } = context; + + const baseComponent = uniteBaseComponent && ( + typeof uniteBaseComponent === 'string' ? uniteBaseComponent : '@icedesign/base'); + + onGetWebpackConfig((config) => { + // 基于主题包的多主题方案 + if (themePackages && Array.isArray(themePackages)) { + const themesCssVars = {}; + const varsPath = !cssVariable && getVariablesPath({ packageName: '@alifd/next', silent: true }); + + let replaceVars = {}; + let defaultScssVars = {}; + let defaultTheme = ''; + // get scss variables and generate css variables + themePackages.forEach(({ name, ...themeData }) => { + const themePath = getVariablesPath({ packageName: name, filename: `variables.${cssVariable ? 'css' : 'scss'}` }); + if (!cssVariable) { + const configData = themeData.themeConfig || {}; + let themeVars: ThemeVars = {}; + let calcVars = {}; + if (varsPath) { + calcVars = getCalcVars(varsPath, themePath, configData); + } + try { + themeVars = getThemeVars(themePath, Object.assign({}, calcVars, configData ), enableColorNames); + } catch (err) { + log.error('get theme variables err:', err); + } + replaceVars = themeVars.scssVars; + defaultScssVars = themeVars.originTheme; + themesCssVars[name] = themeVars.cssVars; + } else if (themePath) { + // read css variables from css file + themesCssVars[name] = replaceBlank(fs.readFileSync(themePath, 'utf-8')); + } + + if (themeData.default) { + defaultTheme = name; + } + }); + defaultTheme = defaultTheme || (themePackages[0] && themePackages[0].name); + addCSSVariableCode({ defaultTheme, config, themesCssVars, rootDir, cssVariable, log }); + + ['scss', 'scss-module'].forEach((rule) => { + if (config.module.rule(rule).uses.get('ice-skin-loader')) { + config.module + .rule(rule) + .use('ice-skin-loader') + .tap((loaderOptions) => { + return { + ...loaderOptions, + themeConfig: { + defaultScssVars, + replaceVars, + ...(loaderOptions.themeConfig || {}), + } + }; + }); + } + }); + } + + if (baseComponent) { + // 统一基础组件包:@ali/ice, @alife/next, @icedesign/base -> @icedesign/base + log.info('uniteBaseComponent 开启,基础包统一到:', baseComponent); + + const alias = { + // @ali/ice -> uniteBaseComponent + '@ali/ice/global.scss': `${baseComponent}/reset.scss`, + '@ali/ice/lib/row$': `${baseComponent}/lib/_components/@alife/next-grid/lib/row.js`, + '@ali/ice/lib/col$': `${baseComponent}/lib/_components/@alife/next-grid/lib/col.js`, + // sass 里 @import '~xxx' + '@ali/ice/base.scss': `${baseComponent}/lib/core/index.scss`, + '@ali/ice': baseComponent, + + // @alife/next -> uniteBaseComponent + '@alife/next/lib/_components/@alife/next-core/lib/index.scss': `${baseComponent}/reset.scss`, + '@alife/next/reset.scss': `${baseComponent}/reset.scss`, + // sass 里 @import '~xxx' + '@alife/next/variables.scss': `${baseComponent}/variables.scss`, + '@alife/next/lib/core/index.scss': `${baseComponent}/lib/core/index.scss`, + '@alife/next': `${baseComponent}`, + + // @icedesign/base -> uniteBaseComponent + '@icedesign/base/reset.scss': `${baseComponent}/reset.scss`, + // sass 里 @import '~xxx' + '@icedesign/base/variables.scss': `${baseComponent}/variables.scss`, + '@icedesign/base/lib/core/index.scss': `${baseComponent}/lib/core/index.scss`, + '@icedesign/base': `${baseComponent}`, + }; + + config.merge({ + resolve: { + alias, + }, + }); + } + + // babel-plugin-import for @alife/next and @icedesign/base + const importConfigs = [{ + libraryName: '@icedesign/base', + style: true, + }, { + libraryName: '@alife/next', + style: true, + }]; + + ['jsx', 'tsx'].forEach((rule) => { + config.module + .rule(rule) + .use('babel-loader') + .tap((babelOptions) => { + const plugins = babelOptions.plugins.concat( + importConfigs.map((itemConfig) => { + return [require.resolve('babel-plugin-import'), itemConfig, itemConfig.libraryName]; + }), + ); + return { + ...babelOptions, + plugins, + }; + }); + }); + + if (usePx2Vw) { + ['css', 'scss', 'scss-module'].forEach(rule => { + config.module + .rule(rule) + .use('postcss-loader') + .tap((loaderOptions) => { + const { plugins = [] } = loaderOptions; + return { + ...loaderOptions, + plugins: [ + ...plugins, + // eslint-disable-next-line + require('postcss-plugin-rpx2vw'), + // eslint-disable-next-line + postcssPluginPx2vw(px2vwOptions), + ], + }; + }); + }); + } + }); +}; + +export default plugin; \ No newline at end of file diff --git a/packages/plugin-fusion/src/postcssPlugins/postcssPluginPx2vw.js b/packages/plugin-fusion-legacy/src/postcssPluginPx2vw.ts similarity index 66% rename from packages/plugin-fusion/src/postcssPlugins/postcssPluginPx2vw.js rename to packages/plugin-fusion-legacy/src/postcssPluginPx2vw.ts index 4ed02773fc..4f657b3ebe 100644 --- a/packages/plugin-fusion/src/postcssPlugins/postcssPluginPx2vw.js +++ b/packages/plugin-fusion-legacy/src/postcssPluginPx2vw.ts @@ -1,29 +1,22 @@ -const postcss = require('postcss'); +import * as postcss from 'postcss'; const pxRegex = /"[^"]+"|'[^']+'|url\([^)]+\)|(\d*\.?\d+)px/g; -const defaults = { +interface PluginOptions { + viewportWidth: number; + viewportUnit: string; + fontViewportUnit: string; + unitPrecision: number; +} + +const defaults: PluginOptions = { viewportWidth: 750, viewportUnit: 'vw', fontViewportUnit: 'vw', unitPrecision: 5, }; -module.exports = postcss.plugin('postcss-px2vw', function(options) { - const opts = Object.assign({}, defaults, options); - return function(root) { - root.walkDecls(function(decl) { - if (decl.value.indexOf('px') === -1) return; - const unit = getUnit(decl.prop, opts); - decl.value = decl.value.replace( - pxRegex, - createPxReplace(opts, unit, opts.viewportWidth), - ); - }); - }; -}); - -function toFixed(number, precision) { +function toFixed(number: number, precision: number): number { // eslint-disable-next-line no-restricted-properties const multiplier = Math.pow(10, precision + 1); const wholeNumber = Math.floor(number * multiplier); @@ -31,8 +24,8 @@ function toFixed(number, precision) { return (Math.round(wholeNumber / 10) * 10) / multiplier; } -function createPxReplace(opts, viewportUnit, viewportSize) { - return function(m, $1) { +function createPxReplace(opts: PluginOptions, viewportUnit: string, viewportSize: number) { + return function(m: string, $1: string) { if (!$1) return m; const pixels = parseFloat($1); const parsedVal = toFixed( @@ -43,8 +36,22 @@ function createPxReplace(opts, viewportUnit, viewportSize) { }; } -function getUnit(prop, opts) { +function getUnit(prop: string, opts: PluginOptions) { return prop.indexOf('font') === -1 ? opts.viewportUnit : opts.fontViewportUnit; } + +export default postcss.plugin('postcss-px2vw', function(options) { + const opts = Object.assign({}, defaults, options); + return function(root) { + root.walkDecls(function(decl) { + if (decl.value.indexOf('px') === -1) return; + const unit = getUnit(decl.prop, opts); + decl.value = decl.value.replace( + pxRegex, + createPxReplace(opts, unit, opts.viewportWidth), + ); + }); + }; +}); diff --git a/packages/plugin-fusion/src/utils/colorNames.js b/packages/plugin-fusion-legacy/src/theme/colorNames.ts similarity index 98% rename from packages/plugin-fusion/src/utils/colorNames.js rename to packages/plugin-fusion-legacy/src/theme/colorNames.ts index 6272b6e85e..3bf575f96a 100644 --- a/packages/plugin-fusion/src/utils/colorNames.js +++ b/packages/plugin-fusion-legacy/src/theme/colorNames.ts @@ -1,7 +1,7 @@ // Big List of Colors // ------------------ // -const colorNames = { +export default { aliceblue: 'f0f8ff', antiquewhite: 'faebd7', aqua: '0ff', @@ -151,6 +151,4 @@ const colorNames = { whitesmoke: 'f5f5f5', yellow: 'ff0', yellowgreen: '9acd32' -}; - -module.exports = colorNames; \ No newline at end of file +}; \ No newline at end of file diff --git a/packages/plugin-fusion/src/utils/getCalcVars.js b/packages/plugin-fusion-legacy/src/theme/getCalcVars.ts similarity index 84% rename from packages/plugin-fusion/src/utils/getCalcVars.js rename to packages/plugin-fusion-legacy/src/theme/getCalcVars.ts index 7b4bc074ce..691fd39fd7 100644 --- a/packages/plugin-fusion/src/utils/getCalcVars.js +++ b/packages/plugin-fusion-legacy/src/theme/getCalcVars.ts @@ -1,6 +1,5 @@ -const path = require('path'); -const resolveSassImport = require('resolve-sass-import'); -const { getSassImplementation } = require('@builder/app-helpers'); +import * as path from 'path'; +import { getSassImplementation } from '@builder/app-helpers'; // regex for match sass variables like: // $color-calculate-xxxx: transparentize($search-simple-dark-bg-color, 1 - $search-simple-dark-bg-opacity) !default; @@ -10,14 +9,16 @@ const SASS_REGEX = /\$color-calculate[\w-]+?:[\s\S]+?;/g; const CSS_REGEX = /\.color-calculate[\w\s-]+?\{[\s\S]+?\}/g; // fix problem with importing absolute paths on windows. -function formatPathForWin(filepath) { +function formatPathForWin(filepath: string) { const isWin = process.platform === 'win32'; return isWin ? filepath.replace(/\\/g, '/') : filepath; -}; +} -module.exports = (varsPath, themePath, themeConfig) => { +export default (varsPath: string, themePath: string, themeConfig: Record) => { let variablesContent = ''; try { + // eslint-disable-next-line global-require + const resolveSassImport = require('resolve-sass-import'); variablesContent = resolveSassImport(varsPath, path.dirname(varsPath)); } catch (err) { console.log(err); @@ -57,7 +58,7 @@ ${calcKeys.map((key) => { const calcCss = cssContent.match(CSS_REGEX); if (calcCss) { // parse `.color-calculate-mask-background{color: #000}` as `calcVars['calculate-color-mask-background'] = '#000'` - calcCss.forEach((item) => { + calcCss.forEach((item: string) => { const [key, value] = item.split('{'); calcVars[key.replace(/\.|\{/g, '').trim()] = value.replace(/;|\}/g, '').replace('color:', '').trim(); }); diff --git a/packages/plugin-fusion/src/utils/getThemeCode.js b/packages/plugin-fusion-legacy/src/theme/getThemeCode.ts similarity index 91% rename from packages/plugin-fusion/src/utils/getThemeCode.js rename to packages/plugin-fusion-legacy/src/theme/getThemeCode.ts index 3411690b28..319d979f43 100644 --- a/packages/plugin-fusion/src/utils/getThemeCode.js +++ b/packages/plugin-fusion-legacy/src/theme/getThemeCode.ts @@ -1,4 +1,4 @@ -module.exports = (themesCssVars, defaultTheme, cssVariable) => { +export default (themesCssVars: Record, defaultTheme: string, cssVariable: boolean) => { const themesDataStr = Object.keys(themesCssVars).map((themeKey) => { const cssVars = themesCssVars[themeKey]; return !cssVariable diff --git a/packages/plugin-fusion/src/utils/getThemeVars.js b/packages/plugin-fusion-legacy/src/theme/getThemeVars.ts similarity index 91% rename from packages/plugin-fusion/src/utils/getThemeVars.js rename to packages/plugin-fusion-legacy/src/theme/getThemeVars.ts index 639196f33f..513caa8ae8 100644 --- a/packages/plugin-fusion/src/utils/getThemeVars.js +++ b/packages/plugin-fusion-legacy/src/theme/getThemeVars.ts @@ -1,8 +1,7 @@ -/* eslint no-useless-escape:0 */ -const fs = require('fs'); -const colorNames = require('./colorNames'); +import * as fs from 'fs'; +import colorNames from './colorNames'; -module.exports = (themeFile, themeConfig, enableColorNames) => { +export default (themeFile: string, themeConfig: Record, enableColorNames: boolean) => { const themeVars = {}; try { const themeStr = fs.readFileSync(themeFile, 'utf8'); diff --git a/packages/plugin-fusion-legacy/tsconfig.json b/packages/plugin-fusion-legacy/tsconfig.json new file mode 100644 index 0000000000..51e2503982 --- /dev/null +++ b/packages/plugin-fusion-legacy/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.settings.json", + "compilerOptions": { + "baseUrl": "./", + "rootDir": "src", + "outDir": "lib" + } +} diff --git a/packages/plugin-fusion/package.json b/packages/plugin-fusion/package.json index 6894d7ac60..1195b2bbf1 100644 --- a/packages/plugin-fusion/package.json +++ b/packages/plugin-fusion/package.json @@ -27,6 +27,10 @@ "webpack-plugin-import": "^0.2.5", "webpack-sources": "^2.0.0" }, + "devDependencies": { + "@types/webpack-sources": "^2.0.0", + "@types/lodash": "^4.0.0" + }, "peerDependencies": { "sass": "^1.0.0", "webpack": "^4.0.0" diff --git a/packages/plugin-fusion/src/utils/convertCharStr.js b/packages/plugin-fusion/src/convertCharStr.ts similarity index 61% rename from packages/plugin-fusion/src/utils/convertCharStr.js rename to packages/plugin-fusion/src/convertCharStr.ts index 01b59c7f1b..69ea25cd39 100644 --- a/packages/plugin-fusion/src/utils/convertCharStr.js +++ b/packages/plugin-fusion/src/convertCharStr.ts @@ -1,4 +1,4 @@ -function convertCharStr2CSS(ch) { +function convertCharStr2CSS(ch: string) { let code = ch.charCodeAt(0).toString(16); while (code.length < 4) { code = `0${code}`; @@ -6,4 +6,4 @@ function convertCharStr2CSS(ch) { return `\\${code}`; } -module.exports = convertCharStr2CSS; \ No newline at end of file +export default convertCharStr2CSS; diff --git a/packages/plugin-fusion/src/index.js b/packages/plugin-fusion/src/index.ts similarity index 96% rename from packages/plugin-fusion/src/index.js rename to packages/plugin-fusion/src/index.ts index d74aec343f..a817674422 100644 --- a/packages/plugin-fusion/src/index.js +++ b/packages/plugin-fusion/src/index.ts @@ -1,12 +1,16 @@ -const fs = require('fs'); -const path = require('path'); -const { upperFirst, camelCase } = require('lodash'); -const WebpackPluginImport = require('webpack-plugin-import'); -const CheckIceComponentsDepsPlugin = require('./webpackPlugins/checkIceComponentsDepPlugin'); -const AppendStyleWebpackPlugin = require('./webpackPlugins/appendStyleWebpackPlugin'); -const getThemeVars = require('./utils/getThemeVars'); -const getThemeCode = require('./utils/getThemeCode'); -const getCalcVars = require('./utils/getCalcVars'); +import * as fs from 'fs'; +import * as path from 'path'; +import { upperFirst, camelCase } from 'lodash'; +import { IPlugin } from 'build-scripts'; +import AppendStyleWebpackPlugin from './webpackPlugins/appendStyleWebpackPlugin'; + +interface PluginOptions { + externalNext: boolean; + cssVariable: boolean; + themePackage: string; + nextLibDir: string; + importOptions: Partial<{libraryDirectory: string; style: any}> +} function normalizeEntry(entry, preparedChunks) { const preparedName = (preparedChunks || []) @@ -54,6 +58,12 @@ function getVariablesPath({ return filePath; } +const plugin: IPlugin = ({ onGetWebpackConfig, log, context }, options) => { + +}; + +export default plugin; + module.exports = async ({ onGetWebpackConfig, log, context, getAllTask }, plugionOptions = {}) => { const { themePackage, diff --git a/packages/plugin-fusion/src/webpackLoaders/unicodeLoader.js b/packages/plugin-fusion/src/webpackLoaders/unicodeLoader.ts similarity index 60% rename from packages/plugin-fusion/src/webpackLoaders/unicodeLoader.js rename to packages/plugin-fusion/src/webpackLoaders/unicodeLoader.ts index 848a1e9e24..adc3d3d436 100644 --- a/packages/plugin-fusion/src/webpackLoaders/unicodeLoader.js +++ b/packages/plugin-fusion/src/webpackLoaders/unicodeLoader.ts @@ -1,6 +1,6 @@ -const convertCharStr2CSS = require('../utils/convertCharStr'); +import convertCharStr2CSS from '../convertCharStr'; -module.exports = (source) => { +export default (source: string) => { return source.replace(/content:\s*(?:'|")([\u0080-\uffff])(?:'|")/g, (str, $1) => { return `content: "${convertCharStr2CSS($1)}"`; }); diff --git a/packages/plugin-fusion/src/webpackPlugins/appendStyleWebpackPlugin.js b/packages/plugin-fusion/src/webpackPlugins/appendStyleWebpackPlugin.ts similarity index 85% rename from packages/plugin-fusion/src/webpackPlugins/appendStyleWebpackPlugin.js rename to packages/plugin-fusion/src/webpackPlugins/appendStyleWebpackPlugin.ts index 8cb08354f2..bf9023a4ce 100644 --- a/packages/plugin-fusion/src/webpackPlugins/appendStyleWebpackPlugin.js +++ b/packages/plugin-fusion/src/webpackPlugins/appendStyleWebpackPlugin.ts @@ -1,15 +1,11 @@ -/* eslint-disable no-underscore-dangle, no-useless-escape */ +import * as fs from 'fs'; +import * as path from 'path'; +import * as assert from 'assert'; +import { ConcatSource, RawSource } from 'webpack-sources'; +import { getSassImplementation } from '@builder/app-helpers'; +import convertCharStr2CSS from '../convertCharStr'; -const assert = require('assert'); -const { ConcatSource, RawSource } = require('webpack-sources'); -const fs = require('fs'); -const path = require('path'); -const { getSassImplementation } = require('@builder/app-helpers'); -const convertCharStr2CSS = require('../utils/convertCharStr'); - -/* eslint no-console: 0 */ - -function compileSass(srcPath, variableFile, coreVarCode) { +function compileSass(srcPath: string, variableFile: string, coreVarCode: string) { srcPath = String(srcPath); let scssContent = ''; const basePath = path.resolve(srcPath, '..'); @@ -57,7 +53,21 @@ function compileSass(srcPath, variableFile, coreVarCode) { return css.replace(/^@charset "UTF-8";/, ''); // 第一行 charset 去掉 } -module.exports = class AppendStylePlugin { +export default class AppendStylePlugin { + private appendPosition: string; + + private type: string; + + private srcFile: string; + + private variableFile: string; + + private compileThemeIcon: string; + + private themeConfig: Record; + + private distMatch: (chunkName: string, compilerEntry: string, entryPoints: any) => boolean; + constructor(options) { assert.ok( Object.prototype.toString.call(options) === '[object Object]', @@ -77,7 +87,7 @@ module.exports = class AppendStylePlugin { : options.distMatch; } - apply(compiler) { + public apply(compiler) { const srcFile = String(this.srcFile); const distMatch = this.distMatch; const variableFile = this.variableFile; @@ -129,7 +139,7 @@ module.exports = class AppendStylePlugin { }); } - wrapFile(compilation, fileName, content) { + private wrapFile(compilation, fileName, content) { // 默认按照底部添加的来 if (this.appendPosition === 'header') { compilation.assets[fileName] = new ConcatSource( @@ -144,7 +154,7 @@ module.exports = class AppendStylePlugin { } } - compileToCSS(srcFile, themeVariableFile) { + private compileToCSS(srcFile, themeVariableFile) { if (this.type === 'sass') { const themeConfig = this.themeConfig || {}; let coreVarCode = ''; diff --git a/packages/plugin-fusion/src/webpackPlugins/checkIceComponentsDepPlugin/bizComponentsVersion.js b/packages/plugin-fusion/src/webpackPlugins/checkIceComponentsDepPlugin/bizComponentsVersion.js deleted file mode 100644 index 474d31ba26..0000000000 --- a/packages/plugin-fusion/src/webpackPlugins/checkIceComponentsDepPlugin/bizComponentsVersion.js +++ /dev/null @@ -1,104 +0,0 @@ -module.exports = { - '@icedesign/balloon-confirm': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@icedesign/container': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@icedesign/data-binder': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@icedesign/ellipsis': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@icedesign/label': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@icedesign/layout': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@icedesign/notification': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@icedesign/panel': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@icedesign/qrcode': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@icedesign/title': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@icedesign/foundation-symbol': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@icedesign/styled-menu': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@icedesign/img': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@ali/ice-staff-selector': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@ali/ice-ice-text-edit': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@ali/ice-box-selector': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@ali/ice-media': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@ali/ice-img-space': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@ali/ice-add-image': { - '0.x': '<2.x', - '1.x': '>=2.x', - }, - '@ali/ice-add-item': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@ali/ice-add-tag': { - '0.x': '<1.x', - '1.x': '>=1.x', - }, - '@ali/ice-bind': { - '0.x': '<1.x', - }, - '@ali/ice-addres': { - '0.x': '<4.x', - }, - '@ali/ice-add-link': { - '0.x': '<1.x', - }, - '@ali/ice-form': { - '0.x': '<1.x', - }, - '@ali/ice-tag-picker': { - '0.x': '<1.x', - }, - '@ali/ice-h5-preview': { - '0.x': '<1.x', - }, -}; diff --git a/packages/plugin-fusion/src/webpackPlugins/checkIceComponentsDepPlugin/deprecatedComponents.js b/packages/plugin-fusion/src/webpackPlugins/checkIceComponentsDepPlugin/deprecatedComponents.js deleted file mode 100644 index eb2cb1d0c1..0000000000 --- a/packages/plugin-fusion/src/webpackPlugins/checkIceComponentsDepPlugin/deprecatedComponents.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - '@ali/ice-bind': '该组件已停止维护,建议迁移至 axios', -}; diff --git a/packages/plugin-fusion/src/webpackPlugins/checkIceComponentsDepPlugin/index.js b/packages/plugin-fusion/src/webpackPlugins/checkIceComponentsDepPlugin/index.js deleted file mode 100644 index 8fdab51914..0000000000 --- a/packages/plugin-fusion/src/webpackPlugins/checkIceComponentsDepPlugin/index.js +++ /dev/null @@ -1,136 +0,0 @@ -/* eslint-disable import/no-dynamic-require */ -/** - * 检测 ICE 组件的依赖问题 - * - 部分代码直接借鉴了 duplicate-package-checker-webpack-plugin - */ -const path = require('path'); -const findRoot = require('find-root'); -const semver = require('semver'); -const bizComponentsVersion = require('./bizComponentsVersion'); -const deprecatedComponents = require('./deprecatedComponents'); - -const depModules = {}; - -module.exports = class CheckDepsPlugin { - constructor(options) { - this.pkg = options.pkg || {}; - this.log = options.log; - } - - apply(compiler) { - compiler.hooks.emit.tapAsync('CheckDepsPlugin', (compilation, callback) => { - compilation.modules.forEach((module) => { - if (!module.resource) { - return; - } - - const closestPackage = getClosestPackage(module.resource); - - // Skip module if no closest package is found - if (!closestPackage) { - return; - } - - const pkg = closestPackage.package; - - if (!depModules[pkg.name]) { - depModules[pkg.name] = [pkg.version]; - } else if (depModules[pkg.name].indexOf(pkg.version) === -1) { - depModules[pkg.name].push(pkg.version); - } - }); - - // 1. 多份基础组件 - const baseComponentDeps = ['@icedesign/base', '@alife/next', '@ali/ice'].filter((name) => depModules[name]); - if (baseComponentDeps.length > 1) { - this.log.warn(`项目依赖了多份基础组件 ${baseComponentDeps},建议通过配置 buildConfig.uniteBaseComponent 优化`); - } - - // 2. 业务组件与基础组件的版本对应关系 - const pkgDirectDeps = this.pkg.dependencies || {}; - const depFdNext = pkgDirectDeps['@alifd/next']; - const depFeNext = pkgDirectDeps['@ali/ice'] || pkgDirectDeps['@icedesign/base'] || pkgDirectDeps['@alife/next']; - - if (depFeNext && !depFdNext) { - // 只依赖了 0.x 的项目应该使用 0.x 的业务组件 - Object.keys(depModules).forEach((moduleName) => { - checkBizComponentVersion({ - npmName: moduleName, - npmVersion: depModules[moduleName][0], - baseVersion: '0.x', - log: this.log, - }); - }); - } - - if (depFdNext && !depFeNext) { - // 只依赖了 1.x 的项目应该使用 1.x 的业务组件 - Object.keys(depModules).forEach((moduleName) => { - checkBizComponentVersion({ - npmName: moduleName, - npmVersion: depModules[moduleName][0], - baseVersion: '1.x', - log: this.log, - }); - }); - } - - // 3. 不维护的业务组件 - Object.keys(depModules).forEach((moduleName) => { - const deprecatedMsg = deprecatedComponents[moduleName]; - if (deprecatedMsg) { - this.log.warn(deprecatedMsg); - } - }); - - callback(); - }); - } -}; - -// Get closest package definition from path -function getClosestPackage(modulePath) { - let root; - let pkg; - - // Catch findRoot or require errors - try { - root = findRoot(modulePath); - // eslint-disable-next-line - pkg = require(path.join(root, 'package.json')); - } catch (e) { - return null; - } - - // If the package.json does not have a name property, try again from - // one level higher. - // https://github.com/jsdnxx/find-root/issues/2 - // https://github.com/date-fns/date-fns/issues/264#issuecomment-265128399 - if (!pkg.name) { - return getClosestPackage(path.resolve(root, '..')); - } - - return { - package: pkg, - path: root, - }; -} - -function checkBizComponentVersion({ npmName, npmVersion, baseVersion, log }) { - if (!bizComponentsVersion[npmName]) { - // 未统计到或者 0.x&1.x 兼容的业务组件 - return; - } - - const semverVersion = bizComponentsVersion[npmName][baseVersion]; - - if (!semverVersion) { - // 没有对应的(未升级) - log.warn(`${npmName} 暂时没有符合基础组件 ${baseVersion} 的版本,建议联系 ICE 团队协助升级`); - } - - if (!semver.satisfies(npmVersion, semverVersion)) { - // 不符合版本 - log.warn(`项目使用的基础组件版本是 ${baseVersion},业务组件 ${npmName}@${npmVersion} 不符合版本要求 ${semverVersion},建议选择正确的组件版本`); - } -} From dd09564a86436cc025faa816bb85c3dec2c6293b Mon Sep 17 00:00:00 2001 From: ClarkXia Date: Tue, 13 Jul 2021 15:16:22 +0800 Subject: [PATCH 2/3] feat: refactor build-plugin-fusion --- packages/build-scripts-config/CHANGELOG.md | 45 --- .../src/config/webpack/webpack.build.js | 45 --- packages/plugin-fusion-legacy/package.json | 16 + packages/plugin-fusion-legacy/src/index.ts | 120 +++++- .../src/theme/getThemeVars.ts | 2 +- .../appendStyleWebpackPlugin.ts | 5 +- .../src/webpackPlugins/convertCharStr.ts | 9 + packages/plugin-fusion/CHANGELOG.md | 4 + packages/plugin-fusion/package.json | 13 +- packages/plugin-fusion/src/index.ts | 354 ++---------------- packages/plugin-fusion/tsconfig.json | 8 + tsconfig.json | 2 + 12 files changed, 177 insertions(+), 446 deletions(-) delete mode 100644 packages/build-scripts-config/CHANGELOG.md delete mode 100644 packages/build-scripts-config/src/config/webpack/webpack.build.js rename packages/{plugin-fusion => plugin-fusion-legacy}/src/webpackPlugins/appendStyleWebpackPlugin.ts (98%) create mode 100644 packages/plugin-fusion-legacy/src/webpackPlugins/convertCharStr.ts create mode 100644 packages/plugin-fusion/tsconfig.json diff --git a/packages/build-scripts-config/CHANGELOG.md b/packages/build-scripts-config/CHANGELOG.md deleted file mode 100644 index 385ee22881..0000000000 --- a/packages/build-scripts-config/CHANGELOG.md +++ /dev/null @@ -1,45 +0,0 @@ -# Changelog - -## 3.0.3 - -- [feat] support webp image file - -## 3.0.2 - -- [fix] lock sass version until it's define a js api for handling warnings - -## 3.0.1 - -- [feat] enable stage 3 for postcss preset env - -## 3.0.0 - -- [feat] upgrate less and less-loader - -## 2.0.0 - -- [feat] remove dependencies of node-sass - -## 1.0.0 - -- [feat] refactor config with @builder/babel-preset-ice - -## 0.1.8 - -- [fix] support css modules hmr - -## 0.1.7 - -- [fix] use `config.path` to set postcss options [#743026](http://gitlab.alibaba-inc.com/a-lib/build-scripts/merge_requests/743026) - -## 0.1.6 - -- [fix] files created before watching start make compile twice - -## 0.1.5 - -- [fix] default option of Terser plugin - -## 0.1.4 - -- [feat] use Terser plugin instead of Uglify plugin. diff --git a/packages/build-scripts-config/src/config/webpack/webpack.build.js b/packages/build-scripts-config/src/config/webpack/webpack.build.js deleted file mode 100644 index ea81ab8740..0000000000 --- a/packages/build-scripts-config/src/config/webpack/webpack.build.js +++ /dev/null @@ -1,45 +0,0 @@ -const TerserPlugin = require('@builder/pack/deps/terser-webpack-plugin'); -const CssMinimizerPlugin = require('@builder/pack/deps/css-minimizer-webpack-plugin'); -const safeParser = require('@builder/pack/deps/postcss-safe-parser'); - -module.exports = (config) => { - // disable devtool of mode prod build - config.devtool(false); - - // uglify js file - config.optimization - .minimizer('TerserPlugin') - .use(TerserPlugin, [{ - parallel: true, - extractComments: false, - terserOptions: { - compress: { - unused: false, - }, - output: { - ascii_only: true, - comments: 'some', - beautify: false, - }, - mangle: true, - }, - }]); - - // optimize css file - config.optimization - .minimizer('CssMinimizerPlugin') - .use(CssMinimizerPlugin, [{ - parallel: false, - minimizerOptions: { - preset: [ - 'default', - { - discardComments: { removeAll: true }, - }, - ], - processorOptions: { - parser: safeParser, - }, - }, - }]); -}; \ No newline at end of file diff --git a/packages/plugin-fusion-legacy/package.json b/packages/plugin-fusion-legacy/package.json index 7a32faa74d..85429bfa12 100644 --- a/packages/plugin-fusion-legacy/package.json +++ b/packages/plugin-fusion-legacy/package.json @@ -10,6 +10,22 @@ "fusion", "build-scripts" ], + "dependencies": { + "@builder/app-helpers": "^2.1.0", + "babel-plugin-import": "^1.11.2", + "ice-skin-loader": "^0.3.0", + "postcss": "^7.0.32", + "postcss-plugin-rpx2vw": "^0.0.2", + "resolve-sass-import": "^0.1.0", + "webpack-sources": "^2.0.0" + }, + "devDependencies": { + "@types/webpack-sources": "^2.0.0" + }, + "peerDependencies": { + "sass": "^1.0.0", + "webpack": "^4.0.0" + }, "author": "", "license": "MIT" } diff --git a/packages/plugin-fusion-legacy/src/index.ts b/packages/plugin-fusion-legacy/src/index.ts index 0fdf3001ea..2a1a3e1701 100644 --- a/packages/plugin-fusion-legacy/src/index.ts +++ b/packages/plugin-fusion-legacy/src/index.ts @@ -2,6 +2,7 @@ import * as path from 'path'; import * as fs from 'fs'; import { IPlugin } from 'build-scripts'; import postcssPluginPx2vw from './postcssPluginPx2vw'; +import AppendStyleWebpackPlugin from './webpackPlugins/appendStyleWebpackPlugin'; import getCalcVars from './theme/getCalcVars'; import getThemeVars from './theme/getThemeVars'; import getThemeCode from './theme/getThemeCode'; @@ -24,10 +25,16 @@ interface PluginOptions { px2vwOptions: object; cssVariable: boolean; enableColorNames: boolean; - themePackages: Partial[]; + themePackage: Partial[] | string; + themeConfig: Record; + uniteNextLib: string; } type GetVariablesPath = (options: { packageName: string; filename?: string; silent?: boolean }) => string; +interface Chunk { + name: string; +} +type NormalizeEntry = (entry: Record, chunks: Chunk[]) => string[]; const getVariablesPath: GetVariablesPath = (packageName, filename = 'variables.scss', silent = false) => { let filePath = ''; @@ -42,6 +49,16 @@ const getVariablesPath: GetVariablesPath = (packageName, filename = 'variables.s return filePath; }; +const normalizeEntry: NormalizeEntry = (entry, preparedChunks) => { + const preparedName = (preparedChunks || []) + .filter((module) => { + return typeof module.name !== 'undefined'; + }) + .map((module) => module.name); + + return Object.keys(entry).concat(preparedName); +}; + function replaceBlank(str: string) { return str.replace(/[\r\n ]/g, '').replace(/\\/g, '\\\\'); } @@ -68,26 +85,39 @@ const plugin: IPlugin = ({ onGetWebpackConfig, log, context }, options) => { uniteBaseComponent, usePx2Vw, px2vwOptions, - themePackages, + themePackage, cssVariable, enableColorNames, + uniteNextLib, + themeConfig, } = (options || {}) as Partial; - const { rootDir } = context; + const { rootDir, webpack, userConfig } = context; + + if (themePackage) { + if (Array.isArray(themePackage)) { + log.info('已启用 themePackage 多主题功能'); + } else { + log.info('使用 Fusion 组件主题包:', themePackage); + } + } + if (themeConfig) { + log.info('自定义 Fusion 组件主题变量:', themeConfig); + } const baseComponent = uniteBaseComponent && ( typeof uniteBaseComponent === 'string' ? uniteBaseComponent : '@icedesign/base'); onGetWebpackConfig((config) => { // 基于主题包的多主题方案 - if (themePackages && Array.isArray(themePackages)) { + let replaceVars = {}; + let defaultScssVars = {}; + let defaultTheme = ''; + if (themePackage && Array.isArray(themePackage)) { const themesCssVars = {}; const varsPath = !cssVariable && getVariablesPath({ packageName: '@alifd/next', silent: true }); - let replaceVars = {}; - let defaultScssVars = {}; - let defaultTheme = ''; // get scss variables and generate css variables - themePackages.forEach(({ name, ...themeData }) => { + themePackage.forEach(({ name, ...themeData }) => { const themePath = getVariablesPath({ packageName: name, filename: `variables.${cssVariable ? 'css' : 'scss'}` }); if (!cssVariable) { const configData = themeData.themeConfig || {}; @@ -113,7 +143,7 @@ const plugin: IPlugin = ({ onGetWebpackConfig, log, context }, options) => { defaultTheme = name; } }); - defaultTheme = defaultTheme || (themePackages[0] && themePackages[0].name); + defaultTheme = defaultTheme || (themePackage[0] && themePackage[0].name); addCSSVariableCode({ defaultTheme, config, themesCssVars, rootDir, cssVariable, log }); ['scss', 'scss-module'].forEach((rule) => { @@ -133,12 +163,70 @@ const plugin: IPlugin = ({ onGetWebpackConfig, log, context }, options) => { }); } }); + } else if (cssVariable && typeof themePackage === 'string') { + // add css variable code when cssVariable is true + const cssVariablePath = getVariablesPath({ packageName: themePackage, filename: 'variables.css'}); + if (cssVariablePath) { + const cssVariables = replaceBlank(fs.readFileSync(cssVariablePath, 'utf-8')); + addCSSVariableCode({ defaultTheme: themePackage, config, themesCssVars: {[themePackage]: cssVariables}, rootDir, cssVariable, log }); + } + } + + const themeFile = typeof themePackage === 'string' && getVariablesPath({ packageName: themePackage, silent: true }); + + ['scss', 'scss-module'].forEach((rule) => { + config.module + .rule(rule) + .use('ice-skin-loader') + .loader(require.resolve('ice-skin-loader')) + .options({ + themeFile, + themeConfig: Object.assign( + {}, + defaultScssVars, + replaceVars, + themeConfig || {} + ), + }); + }); + + // check icons.scss + const iconPackage = (defaultTheme || themePackage) as string; + const iconScssPath = iconPackage && getVariablesPath({ packageName: iconPackage, filename: 'icons.scss', silent: true }); + if (iconScssPath && fs.existsSync(iconScssPath)) { + const appendStylePluginOption = { + type: 'sass', + srcFile: iconScssPath, + variableFile: getVariablesPath({ packageName: iconPackage }), + compileThemeIcon: true, + themeConfig: themeConfig || {}, + distMatch: (chunkName, compilerEntry, compilationPreparedChunks) => { + const entriesAndPreparedChunkNames = normalizeEntry( + compilerEntry, + compilationPreparedChunks, + ); + // 仅对 css 的 chunk 做 处理 + if (entriesAndPreparedChunkNames.length && /\.css$/.test(chunkName)) { + // css/index.css -> index css/index.[hash].css -> index + // css/_component_.usage.css -> _component_.usage + const assetsBaseName = path.basename(chunkName, path.extname(chunkName)); + const assetsFromEntry = userConfig.hash + ? assetsBaseName.substring(0, assetsBaseName.lastIndexOf('.')) + : assetsBaseName; + if (entriesAndPreparedChunkNames.indexOf(assetsFromEntry) !== -1) { + return true; + } + } + return false; + }, + }; + config.plugin('AppendStyleWebpackPlugin').use(AppendStyleWebpackPlugin, [appendStylePluginOption]); } if (baseComponent) { // 统一基础组件包:@ali/ice, @alife/next, @icedesign/base -> @icedesign/base log.info('uniteBaseComponent 开启,基础包统一到:', baseComponent); - + const alias = { // @ali/ice -> uniteBaseComponent '@ali/ice/global.scss': `${baseComponent}/reset.scss`, @@ -171,6 +259,18 @@ const plugin: IPlugin = ({ onGetWebpackConfig, log, context }, options) => { }); } + if (uniteNextLib) { + const replaceRegex = new RegExp(`(alife|alifd)/next/${uniteNextLib === 'es' ? 'lib' : 'es'}`); + config.plugin('UniteNextLib') + .use(webpack.NormalModuleReplacementPlugin, [ + /@(alife|alifd)\/next\/(.*)/, + function(resource) { + // eslint-disable-next-line no-param-reassign + resource.request = resource.request.replace(replaceRegex, `alifd/next/${uniteNextLib}`); + }, + ]); + } + // babel-plugin-import for @alife/next and @icedesign/base const importConfigs = [{ libraryName: '@icedesign/base', diff --git a/packages/plugin-fusion-legacy/src/theme/getThemeVars.ts b/packages/plugin-fusion-legacy/src/theme/getThemeVars.ts index 513caa8ae8..96e7b0effe 100644 --- a/packages/plugin-fusion-legacy/src/theme/getThemeVars.ts +++ b/packages/plugin-fusion-legacy/src/theme/getThemeVars.ts @@ -5,7 +5,7 @@ export default (themeFile: string, themeConfig: Record, enableCo const themeVars = {}; try { const themeStr = fs.readFileSync(themeFile, 'utf8'); - const themeArr = themeStr.match(/\$[\w\-]+?:.+?;/g); + const themeArr = themeStr.match(/\$[\w-]+?:.+?;/g); themeArr.forEach((item) => { const [key, value] = item.split(':'); diff --git a/packages/plugin-fusion/src/webpackPlugins/appendStyleWebpackPlugin.ts b/packages/plugin-fusion-legacy/src/webpackPlugins/appendStyleWebpackPlugin.ts similarity index 98% rename from packages/plugin-fusion/src/webpackPlugins/appendStyleWebpackPlugin.ts rename to packages/plugin-fusion-legacy/src/webpackPlugins/appendStyleWebpackPlugin.ts index bf9023a4ce..514938426e 100644 --- a/packages/plugin-fusion/src/webpackPlugins/appendStyleWebpackPlugin.ts +++ b/packages/plugin-fusion-legacy/src/webpackPlugins/appendStyleWebpackPlugin.ts @@ -1,9 +1,10 @@ +/* eslint-disable no-useless-escape */ import * as fs from 'fs'; import * as path from 'path'; import * as assert from 'assert'; import { ConcatSource, RawSource } from 'webpack-sources'; import { getSassImplementation } from '@builder/app-helpers'; -import convertCharStr2CSS from '../convertCharStr'; +import convertCharStr2CSS from './convertCharStr'; function compileSass(srcPath: string, variableFile: string, coreVarCode: string) { srcPath = String(srcPath); @@ -176,4 +177,4 @@ export default class AppendStylePlugin { } return css; } -}; +} diff --git a/packages/plugin-fusion-legacy/src/webpackPlugins/convertCharStr.ts b/packages/plugin-fusion-legacy/src/webpackPlugins/convertCharStr.ts new file mode 100644 index 0000000000..69ea25cd39 --- /dev/null +++ b/packages/plugin-fusion-legacy/src/webpackPlugins/convertCharStr.ts @@ -0,0 +1,9 @@ +function convertCharStr2CSS(ch: string) { + let code = ch.charCodeAt(0).toString(16); + while (code.length < 4) { + code = `0${code}`; + } + return `\\${code}`; +} + +export default convertCharStr2CSS; diff --git a/packages/plugin-fusion/CHANGELOG.md b/packages/plugin-fusion/CHANGELOG.md index 4355fd5331..91473dc09d 100644 --- a/packages/plugin-fusion/CHANGELOG.md +++ b/packages/plugin-fusion/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 1.0.0 + +- [refactor] remove theme feature to build-plugin-fusion-legacy + ## 0.1.16 - [fix] compatible with umd export when external Next diff --git a/packages/plugin-fusion/package.json b/packages/plugin-fusion/package.json index 1195b2bbf1..9a5d944997 100644 --- a/packages/plugin-fusion/package.json +++ b/packages/plugin-fusion/package.json @@ -14,21 +14,10 @@ "author": "", "license": "MIT", "dependencies": { - "@builder/app-helpers": "^2.1.0", - "babel-plugin-import": "^1.11.2", - "babel-plugin-module-resolver": "^4.0.0", - "find-root": "^1.1.0", - "ice-skin-loader": "^0.3.0", "lodash": "^4.17.15", - "postcss": "^7.0.32", - "postcss-plugin-rpx2vw": "^0.0.2", - "resolve-sass-import": "^0.1.0", - "semver": "^6.1.0", - "webpack-plugin-import": "^0.2.5", - "webpack-sources": "^2.0.0" + "webpack-plugin-import": "^0.2.5" }, "devDependencies": { - "@types/webpack-sources": "^2.0.0", "@types/lodash": "^4.0.0" }, "peerDependencies": { diff --git a/packages/plugin-fusion/src/index.ts b/packages/plugin-fusion/src/index.ts index a817674422..6a45a02c89 100644 --- a/packages/plugin-fusion/src/index.ts +++ b/packages/plugin-fusion/src/index.ts @@ -1,359 +1,49 @@ -import * as fs from 'fs'; -import * as path from 'path'; import { upperFirst, camelCase } from 'lodash'; import { IPlugin } from 'build-scripts'; -import AppendStyleWebpackPlugin from './webpackPlugins/appendStyleWebpackPlugin'; interface PluginOptions { externalNext: boolean; cssVariable: boolean; - themePackage: string; - nextLibDir: string; importOptions: Partial<{libraryDirectory: string; style: any}> } -function normalizeEntry(entry, preparedChunks) { - const preparedName = (preparedChunks || []) - .filter((module) => { - return typeof module.name !== 'undefined'; - }) - .map((module) => module.name); - - return Object.keys(entry).concat(preparedName); -} - -function replaceBlank(str) { - return str.replace(/[\r\n ]/g, '').replace(/\\/g, '\\\\'); -} - -function addCSSVariableCode({ rootDir, themesCssVars, defaultTheme, cssVariable, config, log }) { - try { - const tempDir = path.join(rootDir, './node_modules'); - const jsPath = path.join(tempDir, 'change-theme.js'); - fs.writeFileSync(jsPath, getThemeCode(themesCssVars, defaultTheme, cssVariable)); - - // add theme.js to entry - const entryNames = Object.keys(config.entryPoints.entries()); - entryNames.forEach((name) => { - config.entry(name).prepend(jsPath); - }); - } catch (err) { - log.error('fail to add theme.js to entry'); - log.error(err); - } +interface BabelOptions { + plugins?: [string, ...any][]; + presets?: [string, any][]; } -function getVariablesPath({ - packageName, filename = 'variables.scss', slient = false -}) { - let filePath = ''; - const variables = `${packageName}/${filename}`; - try { - filePath = require.resolve(variables); - } catch (err) { - if (!slient) { - console.log('[ERROR]', `fail to resolve ${variables}`); - } - } - return filePath; -} - -const plugin: IPlugin = ({ onGetWebpackConfig, log, context }, options) => { - -}; - -export default plugin; - -module.exports = async ({ onGetWebpackConfig, log, context, getAllTask }, plugionOptions = {}) => { - const { - themePackage, - themeConfig, - nextLibDir = 'es', - usePx2Vw = false, - px2vwOptions = {}, - style = true, - uniteNextLib, - externalNext, - importOptions = {}, - componentOptions = {}, - enableColorNames, - cssVariable, - } = plugionOptions; - let { uniteBaseComponent } = plugionOptions; - const { rootDir, pkg, userConfig, webpack } = context; - +const plugin: IPlugin = ({ onGetWebpackConfig, getAllTask, context }, options) => { + const { externalNext, cssVariable = true, importOptions = { style: true, libraryDirectory: 'es' } } = (options || {}) as Partial; + const { userConfig } = context; const taskNames = getAllTask(); // ignore externals rule and babel-plugin-import when compile dist const ignoreTasks = ['component-dist']; taskNames.forEach((taskName) => { - onGetWebpackConfig(taskName, (config) => { - // 1. 支持主题能力 - if (themePackage) { - if (Array.isArray(themePackage)) { - log.info('已启用 themePackage 多主题功能'); - } else { - log.info('使用 Fusion 组件主题包:', themePackage); - } - } - if (themeConfig) { - log.info('自定义 Fusion 组件主题变量:', themeConfig); - } - - let replaceVars = {}; - let defaultScssVars = {}; - let defaultTheme = ''; - if (Array.isArray(themePackage)) { - const themesCssVars = {}; - const varsPath = !cssVariable && getVariablesPath({ packageName: '@alifd/next', slient: true }); - // get scss variables and generate css variables - themePackage.forEach(({ name, ...themeData }) => { - const themePath = getVariablesPath({ packageName: name, filename: `variables.${cssVariable ? 'css' : 'scss'}` }); - if (!cssVariable) { - const configData = themeData.themeConfig || {}; - let themeVars = {}; - let calcVars = {}; - if (varsPath) { - calcVars = getCalcVars(varsPath, themePath, configData); - } - try { - themeVars = getThemeVars(themePath, Object.assign({}, calcVars, configData ), enableColorNames); - } catch (err) { - log.error('get theme variables err:', err); - } - replaceVars = themeVars.scssVars; - defaultScssVars = themeVars.originTheme; - themesCssVars[name] = themeVars.cssVars; - } else if (themePath) { - // read css variables from css file - themesCssVars[name] = replaceBlank(fs.readFileSync(themePath, 'utf-8')); - } - - if (themeData.default) { - defaultTheme = name; - } - }); - defaultTheme = defaultTheme || (themePackage[0] && themePackage[0].name); - addCSSVariableCode({ defaultTheme, config, themesCssVars, rootDir, cssVariable, log }); - } else if (cssVariable && themePackage) { - // add css variable code when cssVariable is true - const cssVariablePath = getVariablesPath({ packageName: themePackage, filename: 'variables.css'}); - if (cssVariablePath) { - const cssVariables = replaceBlank(fs.readFileSync(cssVariablePath, 'utf-8')); - addCSSVariableCode({ defaultTheme: themePackage, config, themesCssVars: {[themePackage]: cssVariables}, rootDir, cssVariable, log }); - } - } - - if (usePx2Vw) { - ['css', 'scss', 'scss-module'].forEach(rule => { - config.module - .rule(rule) - .use('postcss-loader') - .tap((options) => { - const { plugins = [] } = options; - return { - ...options, - plugins: [ - ...plugins, - // eslint-disable-next-line - require('postcss-plugin-rpx2vw'), - // eslint-disable-next-line - require('./postcssPlugins/postcssPluginPx2vw')(px2vwOptions), - ], - }; - }); - }); - }; - - const themeFile = typeof themePackage === 'string' && getVariablesPath({ packageName: themePackage, slient: true }); - - ['scss', 'scss-module'].forEach((rule) => { - config.module - .rule(rule) - .use('ice-skin-loader') - .loader(require.resolve('ice-skin-loader')) - .options({ - themeFile, - themeConfig: Object.assign( - {}, - defaultScssVars, - replaceVars, - themeConfig || {} - ), - }); - }); - - // check icons.scss - const iconPackage = defaultTheme || themePackage; - const iconScssPath = iconPackage && getVariablesPath({ packageName: iconPackage, filename: 'icons.scss', slient: true }); - if (iconScssPath && fs.existsSync(iconScssPath)) { - const appendStylePluginOption = { - type: 'sass', - srcFile: iconScssPath, - variableFile: getVariablesPath({ packageName: iconPackage }), - compileThemeIcon: true, - themeConfig: themeConfig || {}, - distMatch: (chunkName, compilerEntry, compilationPreparedChunks) => { - const entriesAndPreparedChunkNames = normalizeEntry( - compilerEntry, - compilationPreparedChunks, - ); - // 仅对 css 的 chunk 做 处理 - if (entriesAndPreparedChunkNames.length && /\.css$/.test(chunkName)) { - // css/index.css -> index css/index.[hash].css -> index - // css/_component_.usage.css -> _component_.usage - const assetsBaseName = path.basename(chunkName, path.extname(chunkName)); - const assetsFromEntry = userConfig.hash - ? assetsBaseName.substring(0, assetsBaseName.lastIndexOf('.')) - : assetsBaseName; - if (entriesAndPreparedChunkNames.indexOf(assetsFromEntry) !== -1) { - return true; - } - } - return false; - }, - }; - config.plugin('AppendStyleWebpackPlugin').use(AppendStyleWebpackPlugin, [appendStylePluginOption]); - } - - const crossendBabelLoader = []; - - if ('componentOptions' in plugionOptions) { - const { bizComponent = [], customPath = '', componentMap = {} } = componentOptions; - const mixBizCom = {}; - - // bizComponent: ['@alifd/anchor', '@alifd/pro-components'], - - if (Array.isArray(bizComponent)) { - bizComponent.forEach(com => { - mixBizCom[com] = `${com}${customPath}`; - }); - } - - // componentMap: { - // '@alifd/pro-components': '@alifd/pro-components/lib/mobile', - // '@alifd/pro-components-2': '@alifd/pro-components-2-mobile' - // } - const mapList = Object.keys(componentMap); - if (mapList.length > 0) { - mapList.forEach(orgName => { - mixBizCom[orgName] = componentMap[orgName]; - }); - } - - crossendBabelLoader.push(require.resolve('babel-plugin-module-resolver'), { - alias: mixBizCom - }); - } - // 2. 组件(包含业务组件)按需加载&样式自动引入 - // babel-plugin-import: 基础组件 - // remove babel-plugin-import if external next + onGetWebpackConfig((config) => { if (!externalNext && !ignoreTasks.includes(taskName)) { - const importConfigs = [{ - libraryName: '@icedesign/base', - style, - }, { - libraryName: '@alife/next', - style, - }, { + const babelPluginImportOptions = { libraryName: '@alifd/next', - libraryDirectory: nextLibDir, - style: cssVariable ? (name) => `${name}/style2` : style, ...importOptions, - }]; + }; + if (cssVariable) { + babelPluginImportOptions.style = (name: string) => `${name}/style2`; + } ['jsx', 'tsx'].forEach((rule) => { config.module .rule(rule) .use('babel-loader') - .tap((options) => { - const plugins = options.plugins.concat( - importConfigs.map((itemConfig) => { - return [require.resolve('babel-plugin-import'), itemConfig, itemConfig.libraryName]; - }), + .tap((babelOptions: BabelOptions) => { + const plugins = babelOptions.plugins.concat( + [require.resolve('babel-plugin-import'), babelPluginImportOptions, babelPluginImportOptions.libraryName] ); - if (crossendBabelLoader.length > 0) { - plugins.push(crossendBabelLoader); - } - options.plugins = plugins; - return options; + return { + ...babelOptions, + plugins, + }; }); }); } - - // 业务组件:不可枚举,使用 webpack-plugin-import,内置逻辑(pkg.componentConfig || pkg.stylePath) - // compatible with build-plugin which do not set up WebpackPluginImport - if (!config.plugins.get('WebpackPluginImport')) { - config.plugin('WebpackPluginImport') - .use(WebpackPluginImport, [[ - // 老的业务组件里没有 stylePath or componentConfig - { - libraryName: /@ali\/ice-.*/, - stylePath: 'style.js', - }, - ]]); - } - - // 3. uniteBaseComponent - // uniteBaseComponent: true => @icedesign/base(兼容老的逻辑) - // uniteBaseComponent: "@alife/next" => @alife/next - uniteBaseComponent = uniteBaseComponent && ( - typeof uniteBaseComponent === 'string' ? uniteBaseComponent : '@icedesign/base' - ); - - if (uniteBaseComponent) { - // 统一基础组件包:@ali/ice, @alife/next, @icedesign/base -> @icedesign/base - log.info('uniteBaseComponent 开启,基础包统一到:', uniteBaseComponent); - - const alias = { - // @ali/ice -> uniteBaseComponent - '@ali/ice/global.scss': `${uniteBaseComponent}/reset.scss`, - '@ali/ice/lib/row$': `${uniteBaseComponent}/lib/_components/@alife/next-grid/lib/row.js`, - '@ali/ice/lib/col$': `${uniteBaseComponent}/lib/_components/@alife/next-grid/lib/col.js`, - // sass 里 @import '~xxx' - '@ali/ice/base.scss': `${uniteBaseComponent}/lib/core/index.scss`, - '@ali/ice': uniteBaseComponent, - - // @alife/next -> uniteBaseComponent - '@alife/next/lib/_components/@alife/next-core/lib/index.scss': `${uniteBaseComponent}/reset.scss`, - '@alife/next/reset.scss': `${uniteBaseComponent}/reset.scss`, - // sass 里 @import '~xxx' - '@alife/next/variables.scss': `${uniteBaseComponent}/variables.scss`, - '@alife/next/lib/core/index.scss': `${uniteBaseComponent}/lib/core/index.scss`, - '@alife/next': `${uniteBaseComponent}`, - - // @icedesign/base -> uniteBaseComponent - '@icedesign/base/reset.scss': `${uniteBaseComponent}/reset.scss`, - // sass 里 @import '~xxx' - '@icedesign/base/variables.scss': `${uniteBaseComponent}/variables.scss`, - '@icedesign/base/lib/core/index.scss': `${uniteBaseComponent}/lib/core/index.scss`, - '@icedesign/base': `${uniteBaseComponent}`, - }; - - config.merge({ - resolve: { - alias, - }, - }); - } - - // 4. 检测组件版本 - config.plugin('CheckIceComponentsDepsPlugin') - .use(CheckIceComponentsDepsPlugin, [{ - pkg, - log, - }]); - - if (uniteNextLib) { - const replaceRegex = new RegExp(`(alife|alifd)/next/${nextLibDir === 'es' ? 'lib' : 'es'}`); - config.plugin('UniteNextLib') - .use(webpack.NormalModuleReplacementPlugin, [ - /@(alife|alifd)\/next\/(.*)/, - function(resource) { - // eslint-disable-next-line no-param-reassign - resource.request = resource.request.replace(replaceRegex, `alifd/next/${nextLibDir}`); - }, - ]); - } - + if (externalNext && !ignoreTasks.includes(taskName)) { const externals = []; if (userConfig.externals) { @@ -362,7 +52,7 @@ module.exports = async ({ onGetWebpackConfig, log, context, getAllTask }, plugio const feNextRegex = /@alife\/next\/(es|lib)\/([-\w+]+)$/; const nextRegex = /@(alife|alifd)\/next\/(es|lib)\/([-\w+]+)$/; const baseRegex = /@icedesign\/base\/lib\/([-\w+]+)$/; - externals.push(function(_context, request, callback) { + externals.push(function(_context: string, request: string, callback: Function) { const isNext = nextRegex.test(request); const isDesignBase = baseRegex.test(request); if (isNext || isDesignBase) { @@ -393,3 +83,5 @@ module.exports = async ({ onGetWebpackConfig, log, context, getAllTask }, plugio }); }); }; + +export default plugin; diff --git a/packages/plugin-fusion/tsconfig.json b/packages/plugin-fusion/tsconfig.json new file mode 100644 index 0000000000..51e2503982 --- /dev/null +++ b/packages/plugin-fusion/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.settings.json", + "compilerOptions": { + "baseUrl": "./", + "rootDir": "src", + "outDir": "lib" + } +} diff --git a/tsconfig.json b/tsconfig.json index 89d956df87..d4c868860f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -39,5 +39,7 @@ { "path": "packages/webpack-dev-mock" }, { "path": "packages/plugin-keep-alive" }, { "path": "packages/plugin-dev-inspector" }, + { "path": "packages/plugin-fusion" }, + { "path": "packages/plugin-fusion-legacy" }, ] } From 65b3c3ab7e8231db29ef6e46d447ee302aa54b0c Mon Sep 17 00:00:00 2001 From: ClarkXia Date: Tue, 13 Jul 2021 15:27:42 +0800 Subject: [PATCH 3/3] fix: optimize code --- packages/plugin-fusion-legacy/package.json | 1 + packages/plugin-fusion-legacy/src/index.ts | 40 ++++++++++++++++++++++ packages/plugin-fusion/src/index.ts | 7 +--- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/packages/plugin-fusion-legacy/package.json b/packages/plugin-fusion-legacy/package.json index 85429bfa12..7acd5750be 100644 --- a/packages/plugin-fusion-legacy/package.json +++ b/packages/plugin-fusion-legacy/package.json @@ -13,6 +13,7 @@ "dependencies": { "@builder/app-helpers": "^2.1.0", "babel-plugin-import": "^1.11.2", + "babel-plugin-module-resolver": "^4.0.0", "ice-skin-loader": "^0.3.0", "postcss": "^7.0.32", "postcss-plugin-rpx2vw": "^0.0.2", diff --git a/packages/plugin-fusion-legacy/src/index.ts b/packages/plugin-fusion-legacy/src/index.ts index 2a1a3e1701..8104a7e5f1 100644 --- a/packages/plugin-fusion-legacy/src/index.ts +++ b/packages/plugin-fusion-legacy/src/index.ts @@ -13,6 +13,12 @@ interface ThemeItem { themeConfig: Record; } +interface ComponentOptions { + bizComponent: string[]; + customPath: string; + componentMap: Record; +} + interface ThemeVars { scssVars?: Record; originTheme?: Record; @@ -28,6 +34,7 @@ interface PluginOptions { themePackage: Partial[] | string; themeConfig: Record; uniteNextLib: string; + componentOptions: ComponentOptions; } type GetVariablesPath = (options: { packageName: string; filename?: string; silent?: boolean }) => string; @@ -90,6 +97,7 @@ const plugin: IPlugin = ({ onGetWebpackConfig, log, context }, options) => { enableColorNames, uniteNextLib, themeConfig, + componentOptions, } = (options || {}) as Partial; const { rootDir, webpack, userConfig } = context; @@ -271,6 +279,35 @@ const plugin: IPlugin = ({ onGetWebpackConfig, log, context }, options) => { ]); } + const crossendBabelLoader = []; + if (componentOptions) { + const { bizComponent = [], customPath = '', componentMap = {} } = componentOptions; + const mixBizCom = {}; + + // bizComponent: ['@alifd/anchor', '@alifd/pro-components'], + + if (Array.isArray(bizComponent)) { + bizComponent.forEach(com => { + mixBizCom[com] = `${com}${customPath}`; + }); + } + + // componentMap: { + // '@alifd/pro-components': '@alifd/pro-components/lib/mobile', + // '@alifd/pro-components-2': '@alifd/pro-components-2-mobile' + // } + const mapList = Object.keys(componentMap); + if (mapList.length > 0) { + mapList.forEach(orgName => { + mixBizCom[orgName] = componentMap[orgName]; + }); + } + + crossendBabelLoader.push(require.resolve('babel-plugin-module-resolver'), { + alias: mixBizCom + }); + } + // babel-plugin-import for @alife/next and @icedesign/base const importConfigs = [{ libraryName: '@icedesign/base', @@ -290,6 +327,9 @@ const plugin: IPlugin = ({ onGetWebpackConfig, log, context }, options) => { return [require.resolve('babel-plugin-import'), itemConfig, itemConfig.libraryName]; }), ); + if (crossendBabelLoader.length > 0) { + plugins.push(crossendBabelLoader); + } return { ...babelOptions, plugins, diff --git a/packages/plugin-fusion/src/index.ts b/packages/plugin-fusion/src/index.ts index 6a45a02c89..440eff6c03 100644 --- a/packages/plugin-fusion/src/index.ts +++ b/packages/plugin-fusion/src/index.ts @@ -7,11 +7,6 @@ interface PluginOptions { importOptions: Partial<{libraryDirectory: string; style: any}> } -interface BabelOptions { - plugins?: [string, ...any][]; - presets?: [string, any][]; -} - const plugin: IPlugin = ({ onGetWebpackConfig, getAllTask, context }, options) => { const { externalNext, cssVariable = true, importOptions = { style: true, libraryDirectory: 'es' } } = (options || {}) as Partial; const { userConfig } = context; @@ -32,7 +27,7 @@ const plugin: IPlugin = ({ onGetWebpackConfig, getAllTask, context }, options) = config.module .rule(rule) .use('babel-loader') - .tap((babelOptions: BabelOptions) => { + .tap((babelOptions) => { const plugins = babelOptions.plugins.concat( [require.resolve('babel-plugin-import'), babelPluginImportOptions, babelPluginImportOptions.libraryName] );