Skip to content

Commit

Permalink
feat: externals plugin for external dependencies mush easier (#6892)
Browse files Browse the repository at this point in the history
* feat: externals plugin for external dependencies mush easier

* fix: optimize code

* fix: optimize code

* fix: example preset react

---------

Co-authored-by: ZeroLing <[email protected]>
  • Loading branch information
ClarkXia and wssgcg1213 authored Jun 13, 2024
1 parent 9926faa commit d073ee5
Show file tree
Hide file tree
Showing 11 changed files with 258 additions and 29 deletions.
5 changes: 5 additions & 0 deletions .changeset/shaggy-cherries-fail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ice/runtime': patch
---

fix: support cdn url in assets manifest
5 changes: 4 additions & 1 deletion examples/app-config/ice.config.mts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { defineConfig } from '@ice/app';
import externals from '@ice/plugin-externals';

export default defineConfig(() => ({}));
export default defineConfig(() => ({
plugins: [externals({ preset: 'react' })]
}));
9 changes: 2 additions & 7 deletions examples/app-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,13 @@
"license": "MIT",
"dependencies": {
"@ice/app": "workspace:*",
"@ice/plugin-auth": "workspace:*",
"@ice/plugin-rax-compat": "workspace:*",
"@ice/plugin-externals": "workspace:*",
"@ice/runtime": "workspace:*",
"@uni/env": "^1.1.0",
"ahooks": "^3.3.8",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.2",
"speed-measure-webpack-plugin": "^1.5.0",
"webpack": "^5.88.0"
"@types/react-dom": "^18.0.2"
}
}
5 changes: 5 additions & 0 deletions packages/plugin-externals/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# @ice/plugin-externals

## 1.0.0

- Initial release
45 changes: 45 additions & 0 deletions packages/plugin-externals/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# @ice/plugin-externals

`@ice/plugin-externals` is a ice.js plugin. It provides a simple way to add externals support to your application.

## Install

```bash
$ npm i @ice/plugin-externals --save-dev
```

## Usage

Set preset `react` to external react in a easy way.

```js
import { defineConfig } from '@ice/app';
import externals from '@ice/plugin-externals';

export default defineConfig(() => ({
plugins: [externals({ preset: 'react' })]
}));
```

Framework will auto add externals of `react` and `react-dom` to your application, and the cdn url will be inject to the document by default.

Also, you can custom externals and cdn url by yourself:

```js
import { defineConfig } from '@ice/app';
import externals from '@ice/plugin-externals';

export default defineConfig(() => ({
plugins: [externals({
externals: {
antd: 'Antd',
},
cdnMap: {
antd: {
development: 'https://unpkg.com/antd/dist/antd.js',
production: 'https://unpkg.com/antd/dist/antd.min.js',
}
}
})]
}));
```
36 changes: 36 additions & 0 deletions packages/plugin-externals/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "@ice/plugin-externals",
"version": "1.0.0",
"description": "plugin to make externals much easier in ice.js",
"files": [
"esm",
"!esm/**/*.map",
"*.d.ts"
],
"type": "module",
"main": "esm/index.js",
"module": "esm/index.js",
"types": "esm/index.d.ts",
"exports": {
".": "./esm/index.js"
},
"sideEffects": false,
"scripts": {
"watch": "tsc -w --sourceMap",
"build": "tsc"
},
"devDependencies": {
"@ice/app": "^3.3.2",
"@ice/runtime": "^1.2.9",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"webpack": "^5.88.0"
},
"repository": {
"type": "http",
"url": "https://github.com/alibaba/ice/tree/master/packages/plugin-externals"
},
"publishConfig": {
"access": "public"
}
}
80 changes: 80 additions & 0 deletions packages/plugin-externals/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import type { Plugin } from '@ice/app/types';
import InjectExternalScriptsWebpackPlugin from './webpack-plugin.js';

const PLUGIN_NAME = '@ice/plugin-externals';

type Preset = 'react';
interface PluginOptions {
preset?: Preset | Preset[];
externals?: Record<string, string>;
cdnMap?: Record<string, {
development: string | string[];
production: string | string[];
}>;
}

const plugin: Plugin = (options: PluginOptions) => ({
name: PLUGIN_NAME,
setup: ({ onGetConfig, context }) => {
const { command } = context;
const reactExternals = {
react: 'React',
'react-dom': 'ReactDOM',
};
const reactCDN = {
react: {
development: 'https://g.alicdn.com/code/lib/react/18.3.1/umd/react.development.js',
production: 'https://g.alicdn.com/code/lib/react/18.3.1/umd/react.production.min.js',
},
'react-dom': {
development: 'https://g.alicdn.com/code/lib/react-dom/18.3.1/umd/react-dom.development.js',
production: 'https://g.alicdn.com/code/lib/react-dom/18.3.1/umd/react-dom.production.min.js',
},
};
onGetConfig((config) => {
config.configureWebpack ??= [];
config.configureWebpack.push((webpackConfig) => {
let externals = options.externals || {};
let cdnMap = options.cdnMap || {};
if (options.preset && options.preset === 'react') {
switch (options.preset) {
case 'react':
externals = {
...reactExternals,
...externals,
};
cdnMap = {
...reactCDN,
...cdnMap,
};
break;
}
}

if (!webpackConfig.externals) {
webpackConfig.externals = externals;
} else if (typeof webpackConfig.externals === 'object') {
webpackConfig.externals = {
...webpackConfig.externals,
...externals,
};
}
const cdnList = [];
Object.keys(cdnMap).forEach((key) => {
const url = command === 'start' ? cdnMap[key].development : cdnMap[key].production;
const urls = Array.isArray(url) ? url : [url];
cdnList.push(...urls);
});
if (cdnList.length > 0) {
// @ts-ignore missmatch type becasue of webpack prebundled.
webpackConfig.plugins.push(new InjectExternalScriptsWebpackPlugin({
externals: cdnList,
}));
}
return webpackConfig;
});
});
},
});

export default plugin;
39 changes: 39 additions & 0 deletions packages/plugin-externals/src/webpack-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import webpack from 'webpack';
import type { Compiler } from 'webpack';

const ASSET_MANIFEST_JSON_NAME = 'assets-manifest.json';

interface PluginOptions {
externals: string[];
}

export default class InjectExternalScriptsWebpackPlugin {
private options: PluginOptions;

constructor(options: PluginOptions) {
this.options = options;
}

apply(compiler: Compiler) {
compiler.hooks.make.tap('InjectExternalScriptsWebpackPlugin', (compilation) => {
compilation.hooks.processAssets.tap(
{
name: 'InjectExternalScriptsWebpackPlugin',
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
},
() => {
const assetsManifest = compilation.assets[ASSET_MANIFEST_JSON_NAME];
if (assetsManifest) {
const json = JSON.parse(assetsManifest.source().toString());
delete compilation.assets[ASSET_MANIFEST_JSON_NAME];
json.entries.main.unshift(...this.options.externals);
compilation.emitAsset(
ASSET_MANIFEST_JSON_NAME,
new webpack.sources.RawSource(JSON.stringify(json)),
);
}
},
);
});
}
}
11 changes: 11 additions & 0 deletions packages/plugin-externals/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"baseUrl": "./",
"rootDir": "src",
"outDir": "esm",
"module": "NodeNext",
"moduleResolution": "NodeNext"
},
"include": ["src"]
}
9 changes: 8 additions & 1 deletion packages/runtime/src/Document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,5 +250,12 @@ export function getEntryAssets(assetsManifest: AssetsManifest): string[] {
result = result.concat(assets);
});

return result.map(filePath => `${publicPath}${filePath}`);
return result.map((filePath: string) => {
const prefixes = ['http:', 'https:', '//'];
if (prefixes.some(prefix => filePath.startsWith(prefix))) {
return filePath;
} else {
return `${publicPath}${filePath}`;
}
});
}
43 changes: 23 additions & 20 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d073ee5

Please sign in to comment.