197 lines
6.7 KiB
TypeScript
197 lines
6.7 KiB
TypeScript
import { VERSION } from '@angular/core';
|
|
import {
|
|
Compiler,
|
|
CopyRspackPlugin,
|
|
DefinePlugin,
|
|
RspackPluginInstance,
|
|
} from '@rspack/core';
|
|
import { posix, relative, resolve } from 'node:path';
|
|
import { getEntryPoints } from '../config/config-utils/entry-points';
|
|
import type {
|
|
I18nOptions,
|
|
NormalizedAngularRspackPluginOptions,
|
|
} from '../models';
|
|
import { loadEsmModule } from '../utils/misc-helpers';
|
|
import { isServeMode } from '../utils/rspack-serve-env';
|
|
import { AngularRspackPlugin } from './angular-rspack-plugin';
|
|
import { AngularSsrDevServer } from './angular-ssr-dev-server';
|
|
import { I18nInlinePlugin } from './i18n-inline-plugin';
|
|
import { IndexHtmlPlugin } from './index-html-plugin';
|
|
import { ProgressPlugin } from './progress-plugin';
|
|
import { RxjsEsmResolutionPlugin } from './rxjs-esm-resolution';
|
|
|
|
export class NgRspackPlugin implements RspackPluginInstance {
|
|
readonly pluginOptions: NormalizedAngularRspackPluginOptions;
|
|
readonly isPlatformServer: boolean;
|
|
readonly i18n: I18nOptions;
|
|
|
|
constructor(
|
|
pluginOptions: NormalizedAngularRspackPluginOptions,
|
|
extraOptions: {
|
|
platform: 'browser' | 'server';
|
|
i18nOptions: I18nOptions;
|
|
}
|
|
) {
|
|
this.pluginOptions = pluginOptions;
|
|
this.i18n = extraOptions.i18nOptions;
|
|
this.isPlatformServer = extraOptions.platform === 'server';
|
|
}
|
|
|
|
apply(compiler: Compiler) {
|
|
const root = this.pluginOptions.root;
|
|
const isDevServer = isServeMode();
|
|
|
|
if (this.isPlatformServer && isDevServer) {
|
|
if (
|
|
this.pluginOptions.ssr &&
|
|
typeof this.pluginOptions.ssr === 'object' &&
|
|
this.pluginOptions.ssr.entry !== undefined
|
|
) {
|
|
new AngularSsrDevServer(this.pluginOptions).apply(compiler);
|
|
}
|
|
}
|
|
if (!isDevServer && this.pluginOptions.progress) {
|
|
new ProgressPlugin(this.isPlatformServer ? 'server' : 'browser').apply(
|
|
compiler
|
|
);
|
|
}
|
|
new DefinePlugin({
|
|
...(this.pluginOptions.optimization.scripts
|
|
? { ngDevMode: 'false' }
|
|
: {}),
|
|
ngJitMode: this.pluginOptions.aot
|
|
? +VERSION.major >= 21
|
|
? 'false'
|
|
: undefined
|
|
: 'true',
|
|
ngServerMode: this.isPlatformServer,
|
|
ngHmrMode:
|
|
this.pluginOptions.devServer?.hmr && isDevServer ? 'true' : 'false',
|
|
...(this.pluginOptions.define ?? {}),
|
|
}).apply(compiler);
|
|
if (this.pluginOptions.assets && !this.isPlatformServer) {
|
|
new CopyRspackPlugin({
|
|
patterns: (this.pluginOptions.assets ?? []).map((asset) => {
|
|
let { input, output = '' } = asset;
|
|
input = resolve(root, input).replace(/\\/g, '/');
|
|
input = input.endsWith('/') ? input : input + '/';
|
|
output = output.endsWith('/') ? output : output + '/';
|
|
|
|
if (output.startsWith('..')) {
|
|
throw new Error(
|
|
'An asset cannot be written to a location outside of the output path.'
|
|
);
|
|
}
|
|
|
|
return {
|
|
context: input,
|
|
to: output.replace(/^\//, ''),
|
|
from: asset.glob,
|
|
noErrorOnMissing: true,
|
|
force: true,
|
|
globOptions: {
|
|
dot: true,
|
|
ignore: [
|
|
'**/.gitkeep',
|
|
'**/.DS_Store',
|
|
'**/Thumbs.db',
|
|
...(asset.ignore ?? []),
|
|
],
|
|
},
|
|
};
|
|
}),
|
|
}).apply(compiler);
|
|
}
|
|
if (this.pluginOptions.extractLicenses) {
|
|
const { LicenseWebpackPlugin } = require('license-webpack-plugin');
|
|
new LicenseWebpackPlugin({
|
|
stats: {
|
|
warnings: false,
|
|
errors: false,
|
|
},
|
|
perChunkOutput: false,
|
|
outputFilename: posix.join(
|
|
relative(
|
|
this.pluginOptions.outputPath.browser,
|
|
this.pluginOptions.outputPath.base
|
|
),
|
|
'3rdpartylicenses.txt'
|
|
),
|
|
skipChildCompilers: true,
|
|
}).apply(compiler as any);
|
|
}
|
|
if (this.i18n.shouldInline) {
|
|
new I18nInlinePlugin(this.pluginOptions, this.i18n).apply(compiler);
|
|
}
|
|
new RxjsEsmResolutionPlugin().apply(compiler);
|
|
new AngularRspackPlugin(this.pluginOptions, this.i18n).apply(compiler);
|
|
if (!this.isPlatformServer && this.pluginOptions.index) {
|
|
new IndexHtmlPlugin({
|
|
indexPath: this.pluginOptions.index.input,
|
|
index: this.pluginOptions.index,
|
|
baseHref: this.pluginOptions.baseHref,
|
|
outputPath: this.pluginOptions.outputPath.browser,
|
|
entrypoints: getEntryPoints(
|
|
this.pluginOptions.globalStyles,
|
|
this.pluginOptions.globalScripts,
|
|
this.pluginOptions.devServer?.hmr
|
|
),
|
|
i18n: this.i18n,
|
|
optimization: this.pluginOptions.optimization,
|
|
postTransform: this.pluginOptions.index.transformer,
|
|
isSsr: !!(
|
|
this.pluginOptions.ssr ||
|
|
this.pluginOptions.prerender ||
|
|
this.pluginOptions.appShell
|
|
),
|
|
deployUrl: this.pluginOptions.deployUrl,
|
|
crossOrigin: this.pluginOptions.crossOrigin,
|
|
sri: this.pluginOptions.subresourceIntegrity,
|
|
// The augment plugin captures this map at construction; IndexHtmlPlugin
|
|
// fills it during the build so SRI builds emit a lazy-chunk integrity
|
|
// importmap (on the Angular versions that support it).
|
|
chunksIntegrity: this.pluginOptions.subresourceIntegrity
|
|
? new Map()
|
|
: undefined,
|
|
}).apply(compiler);
|
|
}
|
|
if (
|
|
this.pluginOptions.devServer.open &&
|
|
isDevServer &&
|
|
!this.isPlatformServer
|
|
) {
|
|
compiler.hooks.afterEmit.tapAsync(
|
|
'AngularRspackPlugin',
|
|
async (_, callback) => {
|
|
const protocol = this.pluginOptions.devServer.ssl ? 'https' : 'http';
|
|
const hostname =
|
|
this.pluginOptions.devServer.host === '0.0.0.0'
|
|
? 'localhost'
|
|
: this.pluginOptions.devServer.host;
|
|
const port = this.pluginOptions.devServer.port;
|
|
const devServerOpts = compiler.options.devServer;
|
|
const publicPath =
|
|
typeof devServerOpts === 'object'
|
|
? devServerOpts.devMiddleware?.publicPath
|
|
: undefined;
|
|
const pathname =
|
|
typeof publicPath === 'string' ? publicPath : undefined;
|
|
|
|
const serverAddress = new URL(`${protocol}://${hostname}:${port}`);
|
|
if (pathname) {
|
|
// Ensure pathname starts with a slash
|
|
serverAddress.pathname = pathname.startsWith('/')
|
|
? pathname
|
|
: `/${pathname}`;
|
|
}
|
|
|
|
const open = (await loadEsmModule<typeof import('open')>('open'))
|
|
.default;
|
|
await open(serverAddress.toString());
|
|
callback();
|
|
}
|
|
);
|
|
}
|
|
}
|
|
}
|