mirror of https://github.com/alibaba/ice.git
feat: optimize output folder (#115)
* feat: optimize folder * fix: assets transform * fix: optimze code * fix: optimize code
This commit is contained in:
parent
54163342ef
commit
b50d478103
|
|
@ -1,11 +1,14 @@
|
|||
import * as React from 'react';
|
||||
import { Link } from 'ice';
|
||||
// @ts-expect-error
|
||||
import url from './ice.png';
|
||||
|
||||
export default function About() {
|
||||
return (
|
||||
<>
|
||||
<h2>About Page</h2>
|
||||
<Link to="/">home</Link>
|
||||
<img src={url} height="40" width="40" />
|
||||
<span className="mark">new</span>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
|
|
@ -34,7 +34,7 @@
|
|||
"devDependencies": {
|
||||
"@ice/types": "^1.0.0",
|
||||
"build-scripts": "^2.0.0-16",
|
||||
"webpack": "^5.69.1",
|
||||
"webpack": "^5.72.0",
|
||||
"webpack-dev-server": "^4.7.4"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +1,23 @@
|
|||
import type { ModifyWebpackConfig } from '@ice/types/esm/config';
|
||||
|
||||
type AssetRuleConfig = [RegExp, Record<string, any>?];
|
||||
type AssetRuleConfig = [RegExp, Record<string, any>?, boolean?];
|
||||
|
||||
function configAssetsRule(config: AssetRuleConfig) {
|
||||
const [test, dataUrl] = config;
|
||||
const [test, dataUrl = {}, inlineLimit = true] = config;
|
||||
|
||||
return {
|
||||
test,
|
||||
type: 'asset',
|
||||
parser: {
|
||||
...(inlineLimit ? {
|
||||
dataUrlCondition: {
|
||||
maxSize: 8 * 1024, // 8kb
|
||||
},
|
||||
} : {}),
|
||||
},
|
||||
generator: {
|
||||
dataUrl,
|
||||
},
|
||||
parser: {
|
||||
dataUrlCondition: {
|
||||
maxSize: 8 * 1024, // 8kb
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -22,19 +26,9 @@ const assets: ModifyWebpackConfig = (config) => {
|
|||
[/\.woff2?$/, { mimetype: 'application/font-woff' }],
|
||||
[/\.ttf$/, { mimetype: 'application/octet-stream' }],
|
||||
[/\.eot$/, { mimetype: 'application/vnd.ms-fontobject' }],
|
||||
[/\.svg$/, { mimetype: 'image/svg+xml' }],
|
||||
[/\.svg$/, { mimetype: 'image/svg+xml' }, false],
|
||||
[/\.(png|jpg|webp|jpeg|gif)$/i],
|
||||
] as AssetRuleConfig[]).map((config) => configAssetsRule(config));
|
||||
config.module.parser = {
|
||||
javascript: {
|
||||
url: 'relative',
|
||||
},
|
||||
};
|
||||
config.module.generator = {
|
||||
asset: {
|
||||
filename: 'assets/[name].[hash:8][ext]',
|
||||
},
|
||||
};
|
||||
config.module.rules.push(...assetsRule);
|
||||
return config;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ function configCSSRule(config: CSSRuleConfig, options: Options) {
|
|||
|
||||
const css: ModifyWebpackConfig = (config, ctx) => {
|
||||
const { supportedBrowsers, publicPath, hashKey } = ctx;
|
||||
|
||||
const cssOutputFolder = 'css';
|
||||
config.module.rules.push(...([
|
||||
['css'],
|
||||
['less', require.resolve('@builder/pack/deps/less-loader'), ({ lessOptions: { javascriptEnabled: true } })],
|
||||
|
|
@ -92,7 +92,7 @@ const css: ModifyWebpackConfig = (config, ctx) => {
|
|||
] as CSSRuleConfig[]).map((config) => configCSSRule(config, { publicPath, browsers: supportedBrowsers })));
|
||||
config.plugins.push(
|
||||
new MiniCssExtractPlugin({
|
||||
filename: hashKey ? `[name]-[${hashKey}].css` : '[name].css',
|
||||
filename: `${cssOutputFolder}/${hashKey ? `[name]-[${hashKey}].css` : '[name].css'}`,
|
||||
// If the warning is triggered, it seen to be unactionable for the user,
|
||||
ignoreOrder: true,
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -143,7 +143,8 @@ const getWebpackConfig: GetWebpackConfig = ({ rootDir, config, commandArgs = {}
|
|||
output: {
|
||||
publicPath,
|
||||
path: path.isAbsolute(outputDir) ? outputDir : path.join(rootDir, outputDir),
|
||||
filename: hashKey ? `[name]-[${hashKey}].js` : '[name].js',
|
||||
filename: `js/${hashKey ? `[name]-[${hashKey}].js` : '[name].js'}`,
|
||||
assetModuleFilename: 'assets/[name].[hash:8][ext]',
|
||||
},
|
||||
context: rootDir,
|
||||
module: {
|
||||
|
|
@ -160,7 +161,8 @@ const getWebpackConfig: GetWebpackConfig = ({ rootDir, config, commandArgs = {}
|
|||
},
|
||||
},
|
||||
watchOptions: {
|
||||
// add a delay before rebuilding once routes changed webpack can not found routes component after it is been deleted
|
||||
// add a delay before rebuilding once routes changed
|
||||
// webpack can not found routes component after it is been deleted
|
||||
aggregateTimeout: 200,
|
||||
ignored: watchIgnoredRegexp,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -25,8 +25,12 @@ const compilationPlugin = (options: Options): UnpluginOptions => {
|
|||
const compileRegex = compileIncludes.map((includeRule) => {
|
||||
return includeRule instanceof RegExp ? includeRule : new RegExp(includeRule);
|
||||
});
|
||||
const extensionRegex = /\.(jsx?|tsx?|mjs)$/;
|
||||
return {
|
||||
name: 'compilation-plugin',
|
||||
transformInclude(id) {
|
||||
return extensionRegex.test(id);
|
||||
},
|
||||
// @ts-expect-error TODO: source map types
|
||||
async transform(source: string, id: string) {
|
||||
if ((/node_modules/.test(id) && !compileRegex.some((regex) => regex.test(id)))) {
|
||||
|
|
@ -34,10 +38,6 @@ const compilationPlugin = (options: Options): UnpluginOptions => {
|
|||
}
|
||||
|
||||
const suffix = (['jsx', 'tsx'] as JSXSuffix[]).find(suffix => new RegExp(`\\.${suffix}?$`).test(id));
|
||||
if (!suffix) {
|
||||
return;
|
||||
}
|
||||
|
||||
const programmaticOptions = {
|
||||
filename: id,
|
||||
sourceMaps: !!sourceMap,
|
||||
|
|
|
|||
|
|
@ -29,8 +29,16 @@ export default class AssetsManifestPlugin {
|
|||
public createAssets(compilation: Compilation) {
|
||||
const entries = {};
|
||||
const pages = {};
|
||||
const assets = {};
|
||||
|
||||
const entrypoints = compilation.entrypoints.values();
|
||||
const assetsInfo = compilation.assetsInfo.values();
|
||||
|
||||
for (const asset of assetsInfo) {
|
||||
if (asset.sourceFilename) {
|
||||
assets[asset.sourceFilename] = asset.contenthash;
|
||||
}
|
||||
}
|
||||
|
||||
for (const entrypoint of entrypoints) {
|
||||
const entryName = entrypoint.name;
|
||||
|
|
@ -50,6 +58,7 @@ export default class AssetsManifestPlugin {
|
|||
publicPath: compilation.outputOptions?.publicPath,
|
||||
entries,
|
||||
pages,
|
||||
assets,
|
||||
};
|
||||
|
||||
const manifestFileName = resolve(this.outputDir, this.fileName);
|
||||
|
|
|
|||
|
|
@ -38,11 +38,12 @@
|
|||
"postcss": "^8.4.7",
|
||||
"postcss-modules": "^4.3.1",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"mrmime": "^1.0.0",
|
||||
"prettier": "^2.5.1",
|
||||
"sass": "^1.49.9",
|
||||
"semver": "^7.3.5",
|
||||
"webpack": "^5.72.0",
|
||||
"temp": "^0.9.4",
|
||||
"webpack": "^5.69.1",
|
||||
"webpack-dev-server": "^4.7.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
import * as path from 'path';
|
||||
import * as mrmime from 'mrmime';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
const ASSET_TYPES = [
|
||||
// images
|
||||
'png',
|
||||
'jpe?g',
|
||||
'gif',
|
||||
'svg',
|
||||
'webp',
|
||||
// fonts
|
||||
'woff2?',
|
||||
'eot',
|
||||
'ttf',
|
||||
];
|
||||
|
||||
const ASSETS_RE = new RegExp(`\\.(${ASSET_TYPES.join('|')})(\\?.*)?$`);
|
||||
|
||||
interface AssetsManifest {
|
||||
publicPath: string;
|
||||
assets?: {
|
||||
[assetPath: string]: string;
|
||||
};
|
||||
}
|
||||
|
||||
const createAssetsPlugin = (manifestPath: string, rootDir: string) => ({
|
||||
name: 'esbuild-assets',
|
||||
setup(build) {
|
||||
const assetsManifest: AssetsManifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
||||
build.onLoad({ filter: ASSETS_RE }, async (args) => {
|
||||
const relativePath = path.relative(rootDir, args.path);
|
||||
let content = await fs.promises.readFile(args.path);
|
||||
let url = '';
|
||||
const contentHash = assetsManifest!.assets[relativePath];
|
||||
if (contentHash) {
|
||||
const basename = path.basename(args.path);
|
||||
const extname = path.extname(basename);
|
||||
const ext = extname.substring(1);
|
||||
const name = basename.slice(0, -extname.length);
|
||||
// assets/[name].[hash:8][ext]
|
||||
url = `${assetsManifest.publicPath}assets/${name}.${contentHash}.${ext}`;
|
||||
} else {
|
||||
url = `data:${mrmime.lookup(args.path)};base64,${content.toString('base64')}`;
|
||||
}
|
||||
return {
|
||||
contents: `export default ${JSON.stringify(url)}`,
|
||||
loader: 'js',
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default createAssetsPlugin;
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import * as path from 'path';
|
||||
import type { Plugin } from '@ice/types';
|
||||
import createAssetsPlugin from '../../esbuild/assets.js';
|
||||
import generateHTML from './ssr/generateHTML.js';
|
||||
import { setupRenderServer } from './ssr/serverRender.js';
|
||||
|
||||
|
|
@ -8,16 +9,20 @@ const webPlugin: Plugin = ({ registerTask, context, onHook }) => {
|
|||
const { ssg = true, ssr = true } = userConfig;
|
||||
const outputDir = path.join(rootDir, 'build');
|
||||
const routeManifest = path.join(rootDir, '.ice/route-manifest.json');
|
||||
const serverEntry = path.join(outputDir, 'server/entry.mjs');
|
||||
const assetsManifest = path.join(rootDir, '.ice/assets-manifest.json');
|
||||
const serverEntry = path.join(outputDir, 'server/index.mjs');
|
||||
let serverCompiler = async () => '';
|
||||
onHook(`before.${command as 'start' | 'build'}.run`, async ({ esbuildCompile }) => {
|
||||
serverCompiler = async () => {
|
||||
await esbuildCompile({
|
||||
entryPoints: [path.join(rootDir, '.ice/entry.server')],
|
||||
outdir: path.join(outputDir, 'server'),
|
||||
outfile: serverEntry,
|
||||
// platform: 'node',
|
||||
format: 'esm',
|
||||
outExtension: { '.js': '.mjs' },
|
||||
plugins: [
|
||||
createAssetsPlugin(assetsManifest, rootDir),
|
||||
],
|
||||
});
|
||||
// timestamp for disable import cache
|
||||
return `${serverEntry}?version=${new Date().getTime()}`;
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ export function createEsbuildCompiler(options: Options) {
|
|||
return includeRule instanceof RegExp ? includeRule : new RegExp(includeRule);
|
||||
}),
|
||||
}),
|
||||
...(buildOptions.plugins || []),
|
||||
...transformPlugins
|
||||
// ignore compilation-plugin while esbuild has it's own transform
|
||||
.filter(({ name }) => name !== 'compilation-plugin')
|
||||
|
|
|
|||
|
|
@ -85,6 +85,9 @@ export interface AssetsManifest {
|
|||
publicPath: string;
|
||||
entries: string[];
|
||||
pages: string[];
|
||||
assets?: {
|
||||
[assetPath: string]: string;
|
||||
};
|
||||
}
|
||||
export interface AppContext {
|
||||
appConfig: AppConfig;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
"@ice/route-manifest": "^1.0.0",
|
||||
"react": "^17.0.2",
|
||||
"unplugin": "^0.3.2",
|
||||
"webpack": "^5.69.1",
|
||||
"webpack": "^5.72.0",
|
||||
"webpack-dev-server": "^4.7.4"
|
||||
}
|
||||
}
|
||||
|
|
@ -134,7 +134,7 @@ importers:
|
|||
regenerator-runtime: ^0.13.9
|
||||
sass: ^1.49.9
|
||||
unplugin: ^0.3.2
|
||||
webpack: ^5.69.1
|
||||
webpack: ^5.72.0
|
||||
webpack-dev-server: ^4.7.4
|
||||
dependencies:
|
||||
'@builder/pack': 0.6.1
|
||||
|
|
@ -188,6 +188,7 @@ importers:
|
|||
fs-extra: ^10.0.0
|
||||
less: ^4.1.2
|
||||
lodash.merge: ^4.6.2
|
||||
mrmime: ^1.0.0
|
||||
postcss: ^8.4.7
|
||||
postcss-modules: ^4.3.1
|
||||
prettier: ^2.5.1
|
||||
|
|
@ -195,7 +196,7 @@ importers:
|
|||
semver: ^7.3.5
|
||||
temp: ^0.9.4
|
||||
unplugin: ^0.3.2
|
||||
webpack: ^5.69.1
|
||||
webpack: ^5.72.0
|
||||
webpack-dev-server: ^4.7.4
|
||||
dependencies:
|
||||
'@builder/pack': 0.6.1
|
||||
|
|
@ -215,6 +216,7 @@ importers:
|
|||
fs-extra: 10.0.1
|
||||
less: 4.1.2
|
||||
lodash.merge: 4.6.2
|
||||
mrmime: 1.0.0
|
||||
postcss: 8.4.12
|
||||
postcss-modules: 4.3.1_postcss@8.4.12
|
||||
prettier: 2.6.2
|
||||
|
|
@ -269,7 +271,7 @@ importers:
|
|||
esbuild: ^0.14.23
|
||||
react: ^17.0.2
|
||||
unplugin: ^0.3.2
|
||||
webpack: ^5.69.1
|
||||
webpack: ^5.72.0
|
||||
webpack-dev-server: ^4.7.4
|
||||
devDependencies:
|
||||
'@ice/route-manifest': link:../route-manifest
|
||||
|
|
@ -4599,7 +4601,6 @@ packages:
|
|||
/mrmime/1.0.0:
|
||||
resolution: {integrity: sha512-a70zx7zFfVO7XpnQ2IX1Myh9yY4UYvfld/dikWRnsXxbyvMcfz+u6UfgNAtH+k2QqtJuzVpv6eLTx1G2+WKZbQ==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/ms/2.0.0:
|
||||
resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=}
|
||||
|
|
@ -6501,7 +6502,7 @@ packages:
|
|||
mime-types: 2.1.35
|
||||
range-parser: 1.2.1
|
||||
schema-utils: 4.0.0
|
||||
webpack: 5.72.0_esbuild@0.14.36
|
||||
webpack: 5.72.0_@swc+core@1.2.165
|
||||
|
||||
/webpack-dev-server/4.8.1_webpack@5.72.0:
|
||||
resolution: {integrity: sha512-dwld70gkgNJa33czmcj/PlKY/nOy/BimbrgZRaR9vDATBQAYgLzggR0nxDtPLJiLrMgZwbE6RRfJ5vnBBasTyg==}
|
||||
|
|
@ -6541,7 +6542,7 @@ packages:
|
|||
serve-index: 1.9.1
|
||||
sockjs: 0.3.24
|
||||
spdy: 4.0.2
|
||||
webpack: 5.72.0_esbuild@0.14.36
|
||||
webpack: 5.72.0_@swc+core@1.2.165
|
||||
webpack-dev-middleware: 5.3.1_webpack@5.72.0
|
||||
ws: 8.5.0
|
||||
transitivePeerDependencies:
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ describe(`build ${example}`, () => {
|
|||
page = res.page;
|
||||
browser = res.browser;
|
||||
expect(await page.$$text('h2')).toStrictEqual(['Home Page']);
|
||||
const bundleContent = fs.readFileSync(path.join(__dirname, `../../examples/${example}/build/index.js`), 'utf-8');
|
||||
const bundleContent = fs.readFileSync(path.join(__dirname, `../../examples/${example}/build/js/index.js`), 'utf-8');
|
||||
expect(bundleContent.includes('__REMOVED__')).toBe(false);
|
||||
}, 120000);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue