chore(web): clean up webpack setup (#7411)
This commit is contained in:
parent
24c9164ec7
commit
9c1ae3a1b0
@ -1,4 +1,3 @@
|
|||||||
import { serializeJson } from '@nrwl/workspace';
|
|
||||||
import {
|
import {
|
||||||
checkFilesDoNotExist,
|
checkFilesDoNotExist,
|
||||||
checkFilesExist,
|
checkFilesExist,
|
||||||
@ -42,20 +41,19 @@ describe('React Applications', () => {
|
|||||||
|
|
||||||
await testGeneratedApp(appName, {
|
await testGeneratedApp(appName, {
|
||||||
checkStyles: true,
|
checkStyles: true,
|
||||||
checkProdBuild: true,
|
|
||||||
checkLinter: true,
|
checkLinter: true,
|
||||||
checkE2E: true,
|
checkE2E: true,
|
||||||
});
|
});
|
||||||
}, 500000);
|
}, 500000);
|
||||||
|
|
||||||
it('should support vendor sourcemaps', () => {
|
it('should support sourcemaps', () => {
|
||||||
const appName = uniq('app');
|
const appName = uniq('app');
|
||||||
|
|
||||||
runCLI(`generate @nrwl/react:app ${appName} --no-interactive`);
|
runCLI(`generate @nrwl/react:app ${appName} --no-interactive`);
|
||||||
|
|
||||||
runCLI(`build ${appName} --sourceMap`);
|
runCLI(`build ${appName} --sourceMap --outputHashing none`);
|
||||||
|
|
||||||
checkFilesExist(`dist/apps/${appName}/vendor.js.map`);
|
checkFilesExist(`dist/apps/${appName}/main.esm.js.map`);
|
||||||
}, 250000);
|
}, 250000);
|
||||||
|
|
||||||
it('should be able to generate a publishable react lib', async () => {
|
it('should be able to generate a publishable react lib', async () => {
|
||||||
@ -114,7 +112,6 @@ describe('React Applications', () => {
|
|||||||
|
|
||||||
await testGeneratedApp(appName, {
|
await testGeneratedApp(appName, {
|
||||||
checkStyles: true,
|
checkStyles: true,
|
||||||
checkProdBuild: false,
|
|
||||||
checkLinter: false,
|
checkLinter: false,
|
||||||
checkE2E: false,
|
checkE2E: false,
|
||||||
});
|
});
|
||||||
@ -145,7 +142,6 @@ describe('React Applications', () => {
|
|||||||
|
|
||||||
await testGeneratedApp(appName, {
|
await testGeneratedApp(appName, {
|
||||||
checkStyles: true,
|
checkStyles: true,
|
||||||
checkProdBuild: false,
|
|
||||||
checkLinter: false,
|
checkLinter: false,
|
||||||
checkE2E: false,
|
checkE2E: false,
|
||||||
});
|
});
|
||||||
@ -185,18 +181,10 @@ describe('React Applications', () => {
|
|||||||
|
|
||||||
await testGeneratedApp(styledComponentsApp, {
|
await testGeneratedApp(styledComponentsApp, {
|
||||||
checkStyles: false,
|
checkStyles: false,
|
||||||
checkProdBuild: true,
|
|
||||||
checkLinter: false,
|
checkLinter: false,
|
||||||
checkE2E: false,
|
checkE2E: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(readFile(`dist/apps/${styledComponentsApp}/main.js`)).toContain(
|
|
||||||
'app__StyledApp'
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
readFile(`dist/apps/${styledComponentsApp}/prod/main.esm.js`)
|
|
||||||
).not.toContain('app__StyledApp');
|
|
||||||
|
|
||||||
const styledJsxApp = uniq('app');
|
const styledJsxApp = uniq('app');
|
||||||
|
|
||||||
runCLI(
|
runCLI(
|
||||||
@ -205,7 +193,6 @@ describe('React Applications', () => {
|
|||||||
|
|
||||||
await testGeneratedApp(styledJsxApp, {
|
await testGeneratedApp(styledJsxApp, {
|
||||||
checkStyles: false,
|
checkStyles: false,
|
||||||
checkProdBuild: false,
|
|
||||||
checkLinter: false,
|
checkLinter: false,
|
||||||
checkE2E: false,
|
checkE2E: false,
|
||||||
});
|
});
|
||||||
@ -218,7 +205,6 @@ describe('React Applications', () => {
|
|||||||
|
|
||||||
await testGeneratedApp(noStylesApp, {
|
await testGeneratedApp(noStylesApp, {
|
||||||
checkStyles: false,
|
checkStyles: false,
|
||||||
checkProdBuild: false,
|
|
||||||
checkLinter: false,
|
checkLinter: false,
|
||||||
checkE2E: false,
|
checkE2E: false,
|
||||||
});
|
});
|
||||||
@ -236,24 +222,23 @@ describe('React Applications', () => {
|
|||||||
|
|
||||||
runCLI(`generate @nrwl/react:app ${appName} --style=css --no-interactive`);
|
runCLI(`generate @nrwl/react:app ${appName} --style=css --no-interactive`);
|
||||||
|
|
||||||
// changing browser suporrt of this application
|
// changing browser support of this application
|
||||||
updateFile(`apps/${appName}/.browserslistrc`, `IE 11`);
|
updateFile(`apps/${appName}/.browserslistrc`, `IE 11`);
|
||||||
|
|
||||||
await testGeneratedApp(appName, {
|
await testGeneratedApp(appName, {
|
||||||
checkStyles: false,
|
checkStyles: false,
|
||||||
checkProdBuild: true,
|
|
||||||
checkLinter: false,
|
checkLinter: false,
|
||||||
checkE2E: false,
|
checkE2E: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const filesToCheck = [
|
const filesToCheck = [
|
||||||
`dist/apps/${appName}/prod/polyfills.es5.js`,
|
`dist/apps/${appName}/polyfills.es5.js`,
|
||||||
`dist/apps/${appName}/prod/main.es5.js`,
|
`dist/apps/${appName}/main.es5.js`,
|
||||||
];
|
];
|
||||||
|
|
||||||
checkFilesExist(...filesToCheck);
|
checkFilesExist(...filesToCheck);
|
||||||
|
|
||||||
expect(readFile(`dist/apps/${appName}/prod/index.html`)).toContain(
|
expect(readFile(`dist/apps/${appName}/index.html`)).toContain(
|
||||||
`<script src="main.esm.js" type="module"></script><script src="main.es5.js" nomodule defer></script>`
|
`<script src="main.esm.js" type="module"></script><script src="main.es5.js" nomodule defer></script>`
|
||||||
);
|
);
|
||||||
}, 250000);
|
}, 250000);
|
||||||
@ -312,7 +297,6 @@ describe('React Applications', () => {
|
|||||||
|
|
||||||
await testGeneratedApp(appName, {
|
await testGeneratedApp(appName, {
|
||||||
checkStyles: true,
|
checkStyles: true,
|
||||||
checkProdBuild: false,
|
|
||||||
checkLinter: false,
|
checkLinter: false,
|
||||||
checkE2E: false,
|
checkE2E: false,
|
||||||
});
|
});
|
||||||
@ -321,7 +305,6 @@ describe('React Applications', () => {
|
|||||||
async function testGeneratedApp(
|
async function testGeneratedApp(
|
||||||
appName,
|
appName,
|
||||||
opts: {
|
opts: {
|
||||||
checkProdBuild: boolean;
|
|
||||||
checkStyles: boolean;
|
checkStyles: boolean;
|
||||||
checkLinter: boolean;
|
checkLinter: boolean;
|
||||||
checkE2E: boolean;
|
checkE2E: boolean;
|
||||||
@ -332,44 +315,22 @@ describe('React Applications', () => {
|
|||||||
expect(lintResults).toContain('All files pass linting.');
|
expect(lintResults).toContain('All files pass linting.');
|
||||||
}
|
}
|
||||||
|
|
||||||
runCLI(`build ${appName}`);
|
runCLI(`build ${appName} --outputHashing none`);
|
||||||
let filesToCheck = [
|
const filesToCheck = [
|
||||||
`dist/apps/${appName}/index.html`,
|
`dist/apps/${appName}/index.html`,
|
||||||
`dist/apps/${appName}/polyfills.js`,
|
`dist/apps/${appName}/runtime.esm.js`,
|
||||||
`dist/apps/${appName}/runtime.js`,
|
`dist/apps/${appName}/polyfills.esm.js`,
|
||||||
`dist/apps/${appName}/vendor.js`,
|
`dist/apps/${appName}/main.esm.js`,
|
||||||
`dist/apps/${appName}/main.js`,
|
|
||||||
];
|
];
|
||||||
if (opts.checkStyles) {
|
if (opts.checkStyles) {
|
||||||
filesToCheck.push(`dist/apps/${appName}/styles.js`);
|
filesToCheck.push(`dist/apps/${appName}/styles.css`);
|
||||||
}
|
}
|
||||||
checkFilesExist(...filesToCheck);
|
checkFilesExist(...filesToCheck);
|
||||||
|
|
||||||
expect(readFile(`dist/apps/${appName}/main.js`)).toContain(
|
if (opts.checkStyles) {
|
||||||
'function App() {'
|
expect(readFile(`dist/apps/${appName}/index.html`)).toContain(
|
||||||
);
|
`<link rel="stylesheet" href="styles.css">`
|
||||||
|
|
||||||
if (opts.checkProdBuild) {
|
|
||||||
const prodOutputPath = `dist/apps/${appName}/prod`;
|
|
||||||
runCLI(
|
|
||||||
`build ${appName} --prod --output-hashing none --outputPath ${prodOutputPath}`
|
|
||||||
);
|
);
|
||||||
filesToCheck = [
|
|
||||||
`${prodOutputPath}/index.html`,
|
|
||||||
`${prodOutputPath}/runtime.esm.js`,
|
|
||||||
`${prodOutputPath}/polyfills.esm.js`,
|
|
||||||
`${prodOutputPath}/main.esm.js`,
|
|
||||||
];
|
|
||||||
if (opts.checkStyles) {
|
|
||||||
filesToCheck.push(`${prodOutputPath}/styles.css`);
|
|
||||||
}
|
|
||||||
checkFilesExist(...filesToCheck);
|
|
||||||
|
|
||||||
if (opts.checkStyles) {
|
|
||||||
expect(readFile(`${prodOutputPath}/index.html`)).toContain(
|
|
||||||
`<link rel="stylesheet" href="styles.css">`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const testResults = await runCLIAsync(`test ${appName}`);
|
const testResults = await runCLIAsync(`test ${appName}`);
|
||||||
@ -385,7 +346,7 @@ describe('React Applications', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('--style option', () => {
|
fdescribe('--style option', () => {
|
||||||
beforeAll(() => newProject());
|
beforeAll(() => newProject());
|
||||||
|
|
||||||
it.each`
|
it.each`
|
||||||
@ -420,11 +381,7 @@ describe('--style option', () => {
|
|||||||
`body { font-family: "Comic Sans MS"; }`
|
`body { font-family: "Comic Sans MS"; }`
|
||||||
);
|
);
|
||||||
|
|
||||||
runCLI(`build ${appName}`);
|
runCLI(`build ${appName} --outputHashing none`);
|
||||||
|
|
||||||
expect(readFile(`dist/apps/${appName}/styles.js`)).toMatch(/Comic Sans MS/);
|
|
||||||
|
|
||||||
runCLI(`build ${appName} --prod --output-hashing none`);
|
|
||||||
|
|
||||||
expect(readFile(`dist/apps/${appName}/styles.css`)).toMatch(
|
expect(readFile(`dist/apps/${appName}/styles.css`)).toMatch(
|
||||||
/Comic Sans MS/
|
/Comic Sans MS/
|
||||||
|
|||||||
@ -24,20 +24,7 @@ describe('Web Components Applications', () => {
|
|||||||
const lintResults = runCLI(`lint ${appName}`);
|
const lintResults = runCLI(`lint ${appName}`);
|
||||||
expect(lintResults).toContain('All files pass linting.');
|
expect(lintResults).toContain('All files pass linting.');
|
||||||
|
|
||||||
runCLI(`build ${appName}`);
|
runCLI(`build ${appName} --outputHashing none`);
|
||||||
checkFilesExist(
|
|
||||||
`dist/apps/${appName}/index.html`,
|
|
||||||
`dist/apps/${appName}/runtime.js`,
|
|
||||||
`dist/apps/${appName}/polyfills.js`,
|
|
||||||
`dist/apps/${appName}/main.js`,
|
|
||||||
`dist/apps/${appName}/styles.js`
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(readFile(`dist/apps/${appName}/main.js`)).toContain(
|
|
||||||
'class AppElement'
|
|
||||||
);
|
|
||||||
|
|
||||||
runCLI(`build ${appName} --prod --output-hashing none`);
|
|
||||||
checkFilesExist(
|
checkFilesExist(
|
||||||
`dist/apps/${appName}/index.html`,
|
`dist/apps/${appName}/index.html`,
|
||||||
`dist/apps/${appName}/runtime.esm.js`,
|
`dist/apps/${appName}/runtime.esm.js`,
|
||||||
@ -93,7 +80,7 @@ describe('Web Components Applications', () => {
|
|||||||
`dist/apps/${appName}/_should_remove.txt`,
|
`dist/apps/${appName}/_should_remove.txt`,
|
||||||
`dist/apps/_should_not_remove.txt`
|
`dist/apps/_should_not_remove.txt`
|
||||||
);
|
);
|
||||||
runCLI(`build ${appName}`);
|
runCLI(`build ${appName} --outputHashing none`);
|
||||||
runCLI(`build ${libName}`);
|
runCLI(`build ${libName}`);
|
||||||
checkFilesDoNotExist(
|
checkFilesDoNotExist(
|
||||||
`dist/apps/${appName}/_should_remove.txt`,
|
`dist/apps/${appName}/_should_remove.txt`,
|
||||||
@ -103,11 +90,11 @@ describe('Web Components Applications', () => {
|
|||||||
|
|
||||||
// `delete-output-path`
|
// `delete-output-path`
|
||||||
createFile(`dist/apps/${appName}/_should_keep.txt`);
|
createFile(`dist/apps/${appName}/_should_keep.txt`);
|
||||||
runCLI(`build ${appName} --delete-output-path=false`);
|
runCLI(`build ${appName} --delete-output-path=false --outputHashing none`);
|
||||||
checkFilesExist(`dist/apps/${appName}/_should_keep.txt`);
|
checkFilesExist(`dist/apps/${appName}/_should_keep.txt`);
|
||||||
|
|
||||||
createFile(`dist/libs/${libName}/_should_keep.txt`);
|
createFile(`dist/libs/${libName}/_should_keep.txt`);
|
||||||
runCLI(`build ${libName} --delete-output-path=false`);
|
runCLI(`build ${libName} --delete-output-path=false --outputHashing none`);
|
||||||
checkFilesExist(`dist/libs/${libName}/_should_keep.txt`);
|
checkFilesExist(`dist/libs/${libName}/_should_keep.txt`);
|
||||||
}, 120000);
|
}, 120000);
|
||||||
|
|
||||||
@ -118,7 +105,7 @@ describe('Web Components Applications', () => {
|
|||||||
|
|
||||||
updateFile(`apps/${appName}/browserslist`, `IE 9-11`);
|
updateFile(`apps/${appName}/browserslist`, `IE 9-11`);
|
||||||
|
|
||||||
runCLI(`build ${appName} --prod --outputHashing=none`);
|
runCLI(`build ${appName} --outputHashing=none`);
|
||||||
|
|
||||||
checkFilesExist(
|
checkFilesExist(
|
||||||
`dist/apps/${appName}/main.esm.js`,
|
`dist/apps/${appName}/main.esm.js`,
|
||||||
@ -159,9 +146,9 @@ describe('Web Components Applications', () => {
|
|||||||
`;
|
`;
|
||||||
return newContent;
|
return newContent;
|
||||||
});
|
});
|
||||||
runCLI(`build ${appName}`);
|
runCLI(`build ${appName} --outputHashing none`);
|
||||||
|
|
||||||
expect(readFile(`dist/apps/${appName}/main.js`)).toMatch(
|
expect(readFile(`dist/apps/${appName}/main.esm.js`)).toMatch(
|
||||||
/Reflect\.metadata/
|
/Reflect\.metadata/
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -172,9 +159,9 @@ describe('Web Components Applications', () => {
|
|||||||
return JSON.stringify(json);
|
return JSON.stringify(json);
|
||||||
});
|
});
|
||||||
|
|
||||||
runCLI(`build ${appName}`);
|
runCLI(`build ${appName} --outputHashing none`);
|
||||||
|
|
||||||
expect(readFile(`dist/apps/${appName}/main.js`)).not.toMatch(
|
expect(readFile(`dist/apps/${appName}/main.esm.js`)).not.toMatch(
|
||||||
/Reflect\.metadata/
|
/Reflect\.metadata/
|
||||||
);
|
);
|
||||||
}, 120000);
|
}, 120000);
|
||||||
@ -254,7 +241,7 @@ describe('CLI - Environment Variables', () => {
|
|||||||
|
|
||||||
updateFile(main2, `${newCode2}\n${content2}`);
|
updateFile(main2, `${newCode2}\n${content2}`);
|
||||||
|
|
||||||
runCLI(`run-many --target=build --all`, {
|
runCLI(`run-many --target build --all --no-optimization`, {
|
||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
NODE_ENV: 'test',
|
NODE_ENV: 'test',
|
||||||
@ -332,13 +319,13 @@ describe('Build Options', () => {
|
|||||||
return config;
|
return config;
|
||||||
});
|
});
|
||||||
|
|
||||||
runCLI(`build ${appName}`);
|
runCLI(`build ${appName} --outputHashing none --optimization false`);
|
||||||
|
|
||||||
const distPath = `dist/apps/${appName}`;
|
const distPath = `dist/apps/${appName}`;
|
||||||
const scripts = readFile(`${distPath}/scripts.js`);
|
const scripts = readFile(`${distPath}/scripts.js`);
|
||||||
const styles = readFile(`${distPath}/styles.js`);
|
const styles = readFile(`${distPath}/styles.css`);
|
||||||
const barScripts = readFile(`${distPath}/${barScriptsBundleName}.js`);
|
const barScripts = readFile(`${distPath}/${barScriptsBundleName}.js`);
|
||||||
const barStyles = readFile(`${distPath}/${barStylesBundleName}.js`);
|
const barStyles = readFile(`${distPath}/${barStylesBundleName}.css`);
|
||||||
|
|
||||||
expect(scripts).toContain(fooJsContent);
|
expect(scripts).toContain(fooJsContent);
|
||||||
expect(scripts).not.toContain(barJsContent);
|
expect(scripts).not.toContain(barJsContent);
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import {
|
|||||||
readFile,
|
readFile,
|
||||||
readJson,
|
readJson,
|
||||||
readProjectConfig,
|
readProjectConfig,
|
||||||
readWorkspaceConfig,
|
|
||||||
removeProject,
|
removeProject,
|
||||||
rmDist,
|
rmDist,
|
||||||
runCLI,
|
runCLI,
|
||||||
@ -669,13 +668,13 @@ describe('print-affected', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(resWithDeps.tasks[0]).toMatchObject({
|
expect(resWithDeps.tasks[0]).toMatchObject({
|
||||||
id: `${myapp}:build`,
|
id: `${myapp}:build:production`,
|
||||||
overrides: {},
|
overrides: {},
|
||||||
target: {
|
target: {
|
||||||
project: myapp,
|
project: myapp,
|
||||||
target: 'build',
|
target: 'build',
|
||||||
},
|
},
|
||||||
command: `${runNx} build ${myapp}`,
|
command: `${runNx} build ${myapp} --configuration production`,
|
||||||
outputs: [`dist/apps/${myapp}`],
|
outputs: [`dist/apps/${myapp}`],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -222,7 +222,7 @@
|
|||||||
"strip-json-comments": "^3.1.1",
|
"strip-json-comments": "^3.1.1",
|
||||||
"style-loader": "^3.3.0",
|
"style-loader": "^3.3.0",
|
||||||
"styled-components": "5.0.0",
|
"styled-components": "5.0.0",
|
||||||
"stylus": "0.54.5",
|
"stylus": "^0.55.0",
|
||||||
"stylus-loader": "^6.2.0",
|
"stylus-loader": "^6.2.0",
|
||||||
"tailwindcss": "^2.2.17",
|
"tailwindcss": "^2.2.17",
|
||||||
"tcp-port-used": "^1.0.2",
|
"tcp-port-used": "^1.0.2",
|
||||||
|
|||||||
@ -12,6 +12,7 @@ export function addProject(host: Tree, options: NormalizedSchema) {
|
|||||||
targets.build = {
|
targets.build = {
|
||||||
builder: '@nrwl/next:build',
|
builder: '@nrwl/next:build',
|
||||||
outputs: ['{options.outputPath}'],
|
outputs: ['{options.outputPath}'],
|
||||||
|
defaultConfiguration: 'production',
|
||||||
options: {
|
options: {
|
||||||
root: options.appProjectRoot,
|
root: options.appProjectRoot,
|
||||||
outputPath: joinPathFragments('dist', options.appProjectRoot),
|
outputPath: joinPathFragments('dist', options.appProjectRoot),
|
||||||
|
|||||||
@ -58,8 +58,7 @@ function getWebpackConfig(config: Configuration) {
|
|||||||
const babelLoader = config.module.rules.find(
|
const babelLoader = config.module.rules.find(
|
||||||
(rule) =>
|
(rule) =>
|
||||||
typeof rule !== 'string' &&
|
typeof rule !== 'string' &&
|
||||||
rule.loader &&
|
rule.loader?.toString().includes('babel-loader')
|
||||||
rule.loader.toString().includes('babel-loader')
|
|
||||||
);
|
);
|
||||||
if (babelLoader && typeof babelLoader !== 'string') {
|
if (babelLoader && typeof babelLoader !== 'string') {
|
||||||
babelLoader.options['plugins'] = [
|
babelLoader.options['plugins'] = [
|
||||||
|
|||||||
@ -263,7 +263,6 @@ Object {
|
|||||||
});
|
});
|
||||||
expect(targetConfig.build.configurations.production).toEqual({
|
expect(targetConfig.build.configurations.production).toEqual({
|
||||||
optimization: true,
|
optimization: true,
|
||||||
extractCss: true,
|
|
||||||
extractLicenses: true,
|
extractLicenses: true,
|
||||||
fileReplacements: [
|
fileReplacements: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { NormalizedSchema } from '../schema';
|
import { NormalizedSchema } from '../schema';
|
||||||
import {
|
import {
|
||||||
joinPathFragments,
|
|
||||||
addProjectConfiguration,
|
addProjectConfiguration,
|
||||||
|
joinPathFragments,
|
||||||
ProjectConfiguration,
|
ProjectConfiguration,
|
||||||
TargetConfiguration,
|
TargetConfiguration,
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
@ -38,6 +38,7 @@ function createBuildTarget(options: NormalizedSchema): TargetConfiguration {
|
|||||||
return {
|
return {
|
||||||
executor: '@nrwl/web:build',
|
executor: '@nrwl/web:build',
|
||||||
outputs: ['{options.outputPath}'],
|
outputs: ['{options.outputPath}'],
|
||||||
|
defaultConfiguration: 'production',
|
||||||
options: {
|
options: {
|
||||||
outputPath: joinPathFragments('dist', options.appProjectRoot),
|
outputPath: joinPathFragments('dist', options.appProjectRoot),
|
||||||
index: joinPathFragments(options.appProjectRoot, 'src/index.html'),
|
index: joinPathFragments(options.appProjectRoot, 'src/index.html'),
|
||||||
@ -84,7 +85,6 @@ function createBuildTarget(options: NormalizedSchema): TargetConfiguration {
|
|||||||
optimization: true,
|
optimization: true,
|
||||||
outputHashing: 'all',
|
outputHashing: 'all',
|
||||||
sourceMap: false,
|
sourceMap: false,
|
||||||
extractCss: true,
|
|
||||||
namedChunks: false,
|
namedChunks: false,
|
||||||
extractLicenses: true,
|
extractLicenses: true,
|
||||||
vendorChunk: false,
|
vendorChunk: false,
|
||||||
|
|||||||
@ -95,7 +95,7 @@
|
|||||||
"source-map": "0.7.3",
|
"source-map": "0.7.3",
|
||||||
"source-map-loader": "^3.0.0",
|
"source-map-loader": "^3.0.0",
|
||||||
"style-loader": "^3.3.0",
|
"style-loader": "^3.3.0",
|
||||||
"stylus": "0.54.5",
|
"stylus": "^0.55.0",
|
||||||
"stylus-loader": "^6.2.0",
|
"stylus-loader": "^6.2.0",
|
||||||
"terser": "4.3.8",
|
"terser": "4.3.8",
|
||||||
"terser-webpack-plugin": "^5.1.1",
|
"terser-webpack-plugin": "^5.1.1",
|
||||||
|
|||||||
@ -15,24 +15,24 @@ import {
|
|||||||
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
||||||
import { readTsConfig } from '@nrwl/workspace/src/utilities/typescript';
|
import { readTsConfig } from '@nrwl/workspace/src/utilities/typescript';
|
||||||
|
|
||||||
import { writeIndexHtml } from '../../utils/third-party/cli-files/utilities/index-file/write-index-html';
|
|
||||||
import { CrossOriginValue } from '../../utils/third-party/cli-files/utilities/index-file/augment-index-html';
|
|
||||||
import { BuildBrowserFeatures } from '../../utils/third-party/utils/build-browser-features';
|
|
||||||
|
|
||||||
import { normalizeWebBuildOptions } from '../../utils/normalize';
|
import { normalizeWebBuildOptions } from '../../utils/normalize';
|
||||||
import { getWebConfig } from '../../utils/web.config';
|
import { getWebConfig } from '../../utils/web.config';
|
||||||
import type { BuildBuilderOptions } from '../../utils/types';
|
import type { BuildBuilderOptions } from '../../utils/shared-models';
|
||||||
import { deleteOutputDir } from '../../utils/delete-output-dir';
|
import { ExtraEntryPoint } from '../../utils/shared-models';
|
||||||
import type { ExtraEntryPoint } from '../../utils/third-party/browser/schema';
|
|
||||||
import { getEmittedFiles, runWebpack } from '../../utils/run-webpack';
|
import { getEmittedFiles, runWebpack } from '../../utils/run-webpack';
|
||||||
|
import { BuildBrowserFeatures } from '../../utils/webpack/build-browser-features';
|
||||||
|
import { deleteOutputDir } from '../../utils/fs';
|
||||||
|
import {
|
||||||
|
CrossOriginValue,
|
||||||
|
writeIndexHtml,
|
||||||
|
} from '../../utils/webpack/write-index-html';
|
||||||
|
|
||||||
export interface WebBuildBuilderOptions extends BuildBuilderOptions {
|
export interface WebBuildExecutorOptions extends BuildBuilderOptions {
|
||||||
index: string;
|
index: string;
|
||||||
budgets?: any[];
|
budgets?: any[];
|
||||||
baseHref?: string;
|
baseHref?: string;
|
||||||
deployUrl?: string;
|
deployUrl?: string;
|
||||||
|
|
||||||
extractCss?: boolean;
|
|
||||||
crossOrigin?: CrossOriginValue;
|
crossOrigin?: CrossOriginValue;
|
||||||
|
|
||||||
polyfills?: string;
|
polyfills?: string;
|
||||||
@ -58,7 +58,7 @@ export interface WebBuildBuilderOptions extends BuildBuilderOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getWebpackConfigs(
|
function getWebpackConfigs(
|
||||||
options: WebBuildBuilderOptions,
|
options: WebBuildExecutorOptions,
|
||||||
context: ExecutorContext
|
context: ExecutorContext
|
||||||
): Configuration[] {
|
): Configuration[] {
|
||||||
const metadata = context.workspace.projects[context.projectName];
|
const metadata = context.workspace.projects[context.projectName];
|
||||||
@ -115,7 +115,7 @@ function getWebpackConfigs(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function* run(
|
export async function* run(
|
||||||
options: WebBuildBuilderOptions,
|
options: WebBuildExecutorOptions,
|
||||||
context: ExecutorContext
|
context: ExecutorContext
|
||||||
) {
|
) {
|
||||||
// Node versions 12.2-12.8 has a bug where prod builds will hang for 2-3 minutes
|
// Node versions 12.2-12.8 has a bug where prod builds will hang for 2-3 minutes
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { map, tap } from 'rxjs/operators';
|
|||||||
import * as WebpackDevServer from 'webpack-dev-server';
|
import * as WebpackDevServer from 'webpack-dev-server';
|
||||||
|
|
||||||
import { normalizeWebBuildOptions } from '../../utils/normalize';
|
import { normalizeWebBuildOptions } from '../../utils/normalize';
|
||||||
import { WebBuildBuilderOptions } from '../build/build.impl';
|
import { WebBuildExecutorOptions } from '../build/build.impl';
|
||||||
import { getDevServerConfig } from '../../utils/devserver.config';
|
import { getDevServerConfig } from '../../utils/devserver.config';
|
||||||
import {
|
import {
|
||||||
calculateProjectDependencies,
|
calculateProjectDependencies,
|
||||||
@ -100,9 +100,9 @@ export default async function* devServerExecutor(
|
|||||||
function getBuildOptions(
|
function getBuildOptions(
|
||||||
options: WebDevServerOptions,
|
options: WebDevServerOptions,
|
||||||
context: ExecutorContext
|
context: ExecutorContext
|
||||||
): WebBuildBuilderOptions {
|
): WebBuildExecutorOptions {
|
||||||
const target = parseTargetString(options.buildTarget);
|
const target = parseTargetString(options.buildTarget);
|
||||||
const overrides: Partial<WebBuildBuilderOptions> = {
|
const overrides: Partial<WebBuildExecutorOptions> = {
|
||||||
watch: false,
|
watch: false,
|
||||||
};
|
};
|
||||||
if (options.maxWorkers) {
|
if (options.maxWorkers) {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { dirname } from 'path';
|
import { dirname } from 'path';
|
||||||
|
|
||||||
import { AssetGlobPattern } from '../../../utils/types';
|
import { AssetGlobPattern } from '../../../utils/shared-models';
|
||||||
import { normalizeAssets, normalizePluginPath } from '../../../utils/normalize';
|
import { normalizeAssets, normalizePluginPath } from '../../../utils/normalize';
|
||||||
import { WebPackageOptions } from '../schema';
|
import { WebPackageOptions } from '../schema';
|
||||||
|
|
||||||
|
|||||||
@ -19,8 +19,7 @@ import {
|
|||||||
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
||||||
import resolve from '@rollup/plugin-node-resolve';
|
import resolve from '@rollup/plugin-node-resolve';
|
||||||
|
|
||||||
import { AssetGlobPattern } from '../../utils/types';
|
import { AssetGlobPattern } from '../../utils/shared-models';
|
||||||
import { deleteOutputDir } from '../../utils/delete-output-dir';
|
|
||||||
import { WebPackageOptions } from './schema';
|
import { WebPackageOptions } from './schema';
|
||||||
import { runRollup } from './lib/run-rollup';
|
import { runRollup } from './lib/run-rollup';
|
||||||
import {
|
import {
|
||||||
@ -28,6 +27,7 @@ import {
|
|||||||
normalizePackageOptions,
|
normalizePackageOptions,
|
||||||
} from './lib/normalize';
|
} from './lib/normalize';
|
||||||
import { analyze } from './lib/analyze-plugin';
|
import { analyze } from './lib/analyze-plugin';
|
||||||
|
import { deleteOutputDir } from '../../utils/fs';
|
||||||
|
|
||||||
// These use require because the ES import isn't correct.
|
// These use require because the ES import isn't correct.
|
||||||
const commonjs = require('@rollup/plugin-commonjs');
|
const commonjs = require('@rollup/plugin-commonjs');
|
||||||
|
|||||||
@ -269,7 +269,6 @@ describe('app', () => {
|
|||||||
});
|
});
|
||||||
expect(architectConfig.build.configurations.production).toEqual({
|
expect(architectConfig.build.configurations.production).toEqual({
|
||||||
optimization: true,
|
optimization: true,
|
||||||
extractCss: true,
|
|
||||||
extractLicenses: true,
|
extractLicenses: true,
|
||||||
fileReplacements: [
|
fileReplacements: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import { cypressProjectGenerator } from '@nrwl/cypress';
|
|||||||
import { Linter, lintProjectGenerator } from '@nrwl/linter';
|
import { Linter, lintProjectGenerator } from '@nrwl/linter';
|
||||||
import { jestProjectGenerator } from '@nrwl/jest';
|
import { jestProjectGenerator } from '@nrwl/jest';
|
||||||
|
|
||||||
import { WebBuildBuilderOptions } from '../../executors/build/build.impl';
|
import { WebBuildExecutorOptions } from '../../executors/build/build.impl';
|
||||||
import { Schema } from './schema';
|
import { Schema } from './schema';
|
||||||
|
|
||||||
interface NormalizedSchema extends Schema {
|
interface NormalizedSchema extends Schema {
|
||||||
@ -50,7 +50,7 @@ function addBuildTarget(
|
|||||||
project: ProjectConfiguration,
|
project: ProjectConfiguration,
|
||||||
options: NormalizedSchema
|
options: NormalizedSchema
|
||||||
): ProjectConfiguration {
|
): ProjectConfiguration {
|
||||||
const buildOptions: WebBuildBuilderOptions = {
|
const buildOptions: WebBuildExecutorOptions = {
|
||||||
outputPath: joinPathFragments('dist', options.appProjectRoot),
|
outputPath: joinPathFragments('dist', options.appProjectRoot),
|
||||||
index: joinPathFragments(options.appProjectRoot, 'src/index.html'),
|
index: joinPathFragments(options.appProjectRoot, 'src/index.html'),
|
||||||
baseHref: '/',
|
baseHref: '/',
|
||||||
@ -66,7 +66,7 @@ function addBuildTarget(
|
|||||||
],
|
],
|
||||||
scripts: [],
|
scripts: [],
|
||||||
};
|
};
|
||||||
const productionBuildOptions: Partial<WebBuildBuilderOptions> = {
|
const productionBuildOptions: Partial<WebBuildExecutorOptions> = {
|
||||||
fileReplacements: [
|
fileReplacements: [
|
||||||
{
|
{
|
||||||
replace: joinPathFragments(
|
replace: joinPathFragments(
|
||||||
@ -82,7 +82,6 @@ function addBuildTarget(
|
|||||||
optimization: true,
|
optimization: true,
|
||||||
outputHashing: 'all',
|
outputHashing: 'all',
|
||||||
sourceMap: false,
|
sourceMap: false,
|
||||||
extractCss: true,
|
|
||||||
namedChunks: false,
|
namedChunks: false,
|
||||||
extractLicenses: true,
|
extractLicenses: true,
|
||||||
vendorChunk: false,
|
vendorChunk: false,
|
||||||
@ -95,6 +94,7 @@ function addBuildTarget(
|
|||||||
build: {
|
build: {
|
||||||
executor: '@nrwl/web:build',
|
executor: '@nrwl/web:build',
|
||||||
outputs: ['{options.outputPath}'],
|
outputs: ['{options.outputPath}'],
|
||||||
|
defaultConfiguration: 'production',
|
||||||
options: buildOptions,
|
options: buildOptions,
|
||||||
configurations: {
|
configurations: {
|
||||||
production: productionBuildOptions,
|
production: productionBuildOptions,
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { Configuration, WebpackPluginInstance } from 'webpack';
|
|||||||
import { LicenseWebpackPlugin } from 'license-webpack-plugin';
|
import { LicenseWebpackPlugin } from 'license-webpack-plugin';
|
||||||
import * as CopyWebpackPlugin from 'copy-webpack-plugin';
|
import * as CopyWebpackPlugin from 'copy-webpack-plugin';
|
||||||
import * as TerserWebpackPlugin from 'terser-webpack-plugin';
|
import * as TerserWebpackPlugin from 'terser-webpack-plugin';
|
||||||
import { AssetGlobPattern, BuildBuilderOptions } from './types';
|
import { AssetGlobPattern, BuildBuilderOptions } from './shared-models';
|
||||||
import { getOutputHashFormat } from './hash-format';
|
import { getOutputHashFormat } from './hash-format';
|
||||||
|
|
||||||
// Inlining tsconfig-paths-webpack-plugin with a patch
|
// Inlining tsconfig-paths-webpack-plugin with a patch
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
import { resolve } from 'path';
|
|
||||||
import { removeSync } from 'fs-extra';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete an output directory, but error out if it's the root of the project.
|
|
||||||
*/
|
|
||||||
export function deleteOutputDir(root: string, outputPath: string) {
|
|
||||||
const resolvedOutputPath = resolve(root, outputPath);
|
|
||||||
if (resolvedOutputPath === root) {
|
|
||||||
throw new Error('Output path MUST not be project root directory!');
|
|
||||||
}
|
|
||||||
|
|
||||||
removeSync(resolvedOutputPath);
|
|
||||||
}
|
|
||||||
@ -3,17 +3,17 @@ import { Configuration as WebpackDevServerConfiguration } from 'webpack-dev-serv
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { getWebConfig } from './web.config';
|
import { getWebConfig } from './web.config';
|
||||||
import { WebBuildBuilderOptions } from '../executors/build/build.impl';
|
import { WebBuildExecutorOptions } from '../executors/build/build.impl';
|
||||||
import { WebDevServerOptions } from '../executors/dev-server/dev-server.impl';
|
import { WebDevServerOptions } from '../executors/dev-server/dev-server.impl';
|
||||||
import { buildServePath } from './serve-path';
|
import { buildServePath } from './serve-path';
|
||||||
import { OptimizationOptions } from './types';
|
import { OptimizationOptions } from './shared-models';
|
||||||
import { readFileSync } from 'fs-extra';
|
import { readFileSync } from 'fs-extra';
|
||||||
|
|
||||||
export function getDevServerConfig(
|
export function getDevServerConfig(
|
||||||
workspaceRoot: string,
|
workspaceRoot: string,
|
||||||
projectRoot: string,
|
projectRoot: string,
|
||||||
sourceRoot: string,
|
sourceRoot: string,
|
||||||
buildOptions: WebBuildBuilderOptions,
|
buildOptions: WebBuildExecutorOptions,
|
||||||
serveOptions: WebDevServerOptions
|
serveOptions: WebDevServerOptions
|
||||||
): Partial<WebpackDevServerConfiguration> {
|
): Partial<WebpackDevServerConfiguration> {
|
||||||
const webpackConfig = getWebConfig(
|
const webpackConfig = getWebConfig(
|
||||||
@ -37,7 +37,7 @@ export function getDevServerConfig(
|
|||||||
function getDevServerPartial(
|
function getDevServerPartial(
|
||||||
root: string,
|
root: string,
|
||||||
options: WebDevServerOptions,
|
options: WebDevServerOptions,
|
||||||
buildOptions: WebBuildBuilderOptions
|
buildOptions: WebBuildExecutorOptions
|
||||||
): WebpackDevServerConfiguration {
|
): WebpackDevServerConfiguration {
|
||||||
const servePath = buildServePath(buildOptions);
|
const servePath = buildServePath(buildOptions);
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ function getDevServerPartial(
|
|||||||
port: options.port,
|
port: options.port,
|
||||||
headers: { 'Access-Control-Allow-Origin': '*' },
|
headers: { 'Access-Control-Allow-Origin': '*' },
|
||||||
historyApiFallback: {
|
historyApiFallback: {
|
||||||
index: `${servePath}/${path.basename(buildOptions.index)}`,
|
index: `${servePath}${path.basename(buildOptions.index)}`,
|
||||||
disableDotRule: true,
|
disableDotRule: true,
|
||||||
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,13 +1,5 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
import { existsSync } from 'fs';
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { isDirectory } from './is-directory';
|
import { existsSync, removeSync, statSync } from 'fs-extra';
|
||||||
|
|
||||||
export function findUp(
|
export function findUp(
|
||||||
names: string | string[],
|
names: string | string[],
|
||||||
@ -60,3 +52,23 @@ export function findAllNodeModules(from: string, root?: string) {
|
|||||||
|
|
||||||
return nodeModules;
|
return nodeModules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an output directory, but error out if it's the root of the project.
|
||||||
|
*/
|
||||||
|
export function deleteOutputDir(root: string, outputPath: string) {
|
||||||
|
const resolvedOutputPath = path.resolve(root, outputPath);
|
||||||
|
if (resolvedOutputPath === root) {
|
||||||
|
throw new Error('Output path MUST not be project root directory!');
|
||||||
|
}
|
||||||
|
|
||||||
|
removeSync(resolvedOutputPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDirectory(path: string) {
|
||||||
|
try {
|
||||||
|
return statSync(path).isDirectory();
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,3 @@
|
|||||||
// Originally from devkit.
|
|
||||||
// See: https://github.com/angular/angular-cli/blob/2c8b12f/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/utils.ts
|
|
||||||
export interface HashFormat {
|
export interface HashFormat {
|
||||||
chunk: string;
|
chunk: string;
|
||||||
extract: string;
|
extract: string;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { normalizeBuildOptions } from './normalize';
|
import { normalizeBuildOptions } from './normalize';
|
||||||
import { BuildBuilderOptions } from './types';
|
import { BuildBuilderOptions } from './shared-models';
|
||||||
|
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,12 @@
|
|||||||
import { WebBuildBuilderOptions } from '../executors/build/build.impl';
|
import { WebBuildExecutorOptions } from '../executors/build/build.impl';
|
||||||
import { normalizePath } from '@nrwl/devkit';
|
import { normalizePath } from '@nrwl/devkit';
|
||||||
import { basename, dirname, relative, resolve } from 'path';
|
import { basename, dirname, relative, resolve } from 'path';
|
||||||
import { AssetGlobPattern, BuildBuilderOptions } from './types';
|
import {
|
||||||
|
AssetGlobPattern,
|
||||||
|
BuildBuilderOptions,
|
||||||
|
ExtraEntryPoint,
|
||||||
|
ExtraEntryPointClass,
|
||||||
|
} from './shared-models';
|
||||||
import { statSync } from 'fs';
|
import { statSync } from 'fs';
|
||||||
|
|
||||||
export interface FileReplacement {
|
export interface FileReplacement {
|
||||||
@ -96,10 +101,10 @@ function normalizeFileReplacements(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeWebBuildOptions(
|
export function normalizeWebBuildOptions(
|
||||||
options: WebBuildBuilderOptions,
|
options: WebBuildExecutorOptions,
|
||||||
root: string,
|
root: string,
|
||||||
sourceRoot: string
|
sourceRoot: string
|
||||||
): WebBuildBuilderOptions {
|
): WebBuildExecutorOptions {
|
||||||
return {
|
return {
|
||||||
...normalizeBuildOptions(options, root, sourceRoot),
|
...normalizeBuildOptions(options, root, sourceRoot),
|
||||||
optimization:
|
optimization:
|
||||||
@ -116,7 +121,9 @@ export function normalizeWebBuildOptions(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertBuildOptions(buildOptions: WebBuildBuilderOptions): any {
|
export function convertBuildOptions(
|
||||||
|
buildOptions: WebBuildExecutorOptions
|
||||||
|
): any {
|
||||||
const options = buildOptions as any;
|
const options = buildOptions as any;
|
||||||
return <any>{
|
return <any>{
|
||||||
...options,
|
...options,
|
||||||
@ -125,3 +132,42 @@ export function convertBuildOptions(buildOptions: WebBuildBuilderOptions): any {
|
|||||||
lazyModules: [] as string[],
|
lazyModules: [] as string[],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NormalizedEntryPoint = Required<Omit<ExtraEntryPointClass, 'lazy'>>;
|
||||||
|
|
||||||
|
export function normalizeExtraEntryPoints(
|
||||||
|
extraEntryPoints: ExtraEntryPoint[],
|
||||||
|
defaultBundleName: string
|
||||||
|
): NormalizedEntryPoint[] {
|
||||||
|
return extraEntryPoints.map((entry) => {
|
||||||
|
let normalizedEntry;
|
||||||
|
if (typeof entry === 'string') {
|
||||||
|
normalizedEntry = {
|
||||||
|
input: entry,
|
||||||
|
inject: true,
|
||||||
|
bundleName: defaultBundleName,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const { lazy, inject = true, ...newEntry } = entry;
|
||||||
|
const injectNormalized = entry.lazy !== undefined ? !entry.lazy : inject;
|
||||||
|
let bundleName;
|
||||||
|
|
||||||
|
if (entry.bundleName) {
|
||||||
|
bundleName = entry.bundleName;
|
||||||
|
} else if (!injectNormalized) {
|
||||||
|
// Lazy entry points use the file name as bundle name.
|
||||||
|
bundleName = basename(
|
||||||
|
normalizePath(
|
||||||
|
entry.input.replace(/\.(js|css|scss|sass|less|styl)$/i, '')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
bundleName = defaultBundleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizedEntry = { ...newEntry, inject: injectNormalized, bundleName };
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedEntry;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { WebBuildBuilderOptions } from '../executors/build/build.impl';
|
import { WebBuildExecutorOptions } from '../executors/build/build.impl';
|
||||||
|
|
||||||
export function buildServePath(browserOptions: WebBuildBuilderOptions) {
|
export function buildServePath(browserOptions: WebBuildExecutorOptions) {
|
||||||
let servePath =
|
let servePath =
|
||||||
_findDefaultServePath(browserOptions.baseHref, browserOptions.deployUrl) ||
|
_findDefaultServePath(browserOptions.baseHref, browserOptions.deployUrl) ||
|
||||||
'/';
|
'/';
|
||||||
|
|||||||
120
packages/web/src/utils/shared-models.ts
Normal file
120
packages/web/src/utils/shared-models.ts
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
import { WebBuildExecutorOptions } from '../executors/build/build.impl';
|
||||||
|
import { FileReplacement } from './normalize';
|
||||||
|
|
||||||
|
export interface OptimizationOptions {
|
||||||
|
scripts: boolean;
|
||||||
|
styles: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BuildBuilderOptions {
|
||||||
|
main: string;
|
||||||
|
outputPath: string;
|
||||||
|
tsConfig: string;
|
||||||
|
watch?: boolean;
|
||||||
|
sourceMap?: boolean | 'hidden';
|
||||||
|
optimization?: boolean | OptimizationOptions;
|
||||||
|
memoryLimit?: number;
|
||||||
|
maxWorkers?: number;
|
||||||
|
poll?: number;
|
||||||
|
|
||||||
|
fileReplacements?: FileReplacement[];
|
||||||
|
assets?: any[];
|
||||||
|
|
||||||
|
progress?: boolean;
|
||||||
|
statsJson?: boolean;
|
||||||
|
extractLicenses?: boolean;
|
||||||
|
verbose?: boolean;
|
||||||
|
|
||||||
|
outputHashing?: any;
|
||||||
|
webpackConfig?: string;
|
||||||
|
|
||||||
|
root?: string;
|
||||||
|
sourceRoot?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssetGlobPattern {
|
||||||
|
glob: string;
|
||||||
|
input: string;
|
||||||
|
output: string;
|
||||||
|
ignore?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AssetPattern = AssetPatternClass | string;
|
||||||
|
|
||||||
|
export interface AssetPatternClass {
|
||||||
|
glob: string;
|
||||||
|
ignore?: string[];
|
||||||
|
input: string;
|
||||||
|
output: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Type {
|
||||||
|
All = 'all',
|
||||||
|
AllScript = 'allScript',
|
||||||
|
Any = 'any',
|
||||||
|
AnyComponentStyle = 'anyComponentStyle',
|
||||||
|
AnyScript = 'anyScript',
|
||||||
|
Bundle = 'bundle',
|
||||||
|
Initial = 'initial',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CrossOrigin {
|
||||||
|
Anonymous = 'anonymous',
|
||||||
|
None = 'none',
|
||||||
|
UseCredentials = 'use-credentials',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IndexUnion = IndexObject | string;
|
||||||
|
|
||||||
|
export interface IndexObject {
|
||||||
|
input: string;
|
||||||
|
output?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Localize = string[] | boolean;
|
||||||
|
|
||||||
|
export type OptimizationUnion = boolean | OptimizationClass;
|
||||||
|
|
||||||
|
export interface OptimizationClass {
|
||||||
|
scripts?: boolean;
|
||||||
|
styles?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum OutputHashing {
|
||||||
|
All = 'all',
|
||||||
|
Bundles = 'bundles',
|
||||||
|
Media = 'media',
|
||||||
|
None = 'none',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ExtraEntryPoint = ExtraEntryPointClass | string;
|
||||||
|
|
||||||
|
export interface ExtraEntryPointClass {
|
||||||
|
bundleName?: string;
|
||||||
|
inject?: boolean;
|
||||||
|
input: string;
|
||||||
|
lazy?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SourceMapUnion = boolean | SourceMapClass;
|
||||||
|
|
||||||
|
export interface SourceMapClass {
|
||||||
|
hidden?: boolean;
|
||||||
|
scripts?: boolean;
|
||||||
|
styles?: boolean;
|
||||||
|
vendor?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StylePreprocessorOptions {
|
||||||
|
includePaths?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WebpackConfigOptions<T = WebBuildExecutorOptions> {
|
||||||
|
root: string;
|
||||||
|
projectRoot: string;
|
||||||
|
sourceRoot?: string;
|
||||||
|
buildOptions: T;
|
||||||
|
tsConfig: any;
|
||||||
|
tsConfigPath: string;
|
||||||
|
supportES2015: boolean;
|
||||||
|
}
|
||||||
425
packages/web/src/utils/third-party/browser/schema.ts
vendored
425
packages/web/src/utils/third-party/browser/schema.ts
vendored
@ -1,425 +0,0 @@
|
|||||||
// THIS FILE IS AUTOMATICALLY GENERATED. TO UPDATE THIS FILE YOU NEED TO CHANGE THE
|
|
||||||
// CORRESPONDING JSON SCHEMA FILE, THEN RUN devkit-admin build (or bazel build ...).
|
|
||||||
|
|
||||||
// tslint:disable:no-global-tslint-disable
|
|
||||||
// tslint:disable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Browser target options
|
|
||||||
*/
|
|
||||||
export interface Schema {
|
|
||||||
/**
|
|
||||||
* Build using Ahead of Time compilation.
|
|
||||||
*/
|
|
||||||
aot?: boolean;
|
|
||||||
/**
|
|
||||||
* List of static application assets.
|
|
||||||
*/
|
|
||||||
assets?: AssetPattern[];
|
|
||||||
/**
|
|
||||||
* Base url for the application being built.
|
|
||||||
*/
|
|
||||||
baseHref?: string;
|
|
||||||
/**
|
|
||||||
* Budget thresholds to ensure parts of your application stay within boundaries which you
|
|
||||||
* set.
|
|
||||||
*/
|
|
||||||
budgets?: Budget[];
|
|
||||||
/**
|
|
||||||
* Enables '@angular-devkit/build-optimizer' optimizations when using the 'aot' option.
|
|
||||||
*/
|
|
||||||
buildOptimizer?: boolean;
|
|
||||||
/**
|
|
||||||
* Use a separate bundle containing code used across multiple bundles.
|
|
||||||
*/
|
|
||||||
commonChunk?: boolean;
|
|
||||||
/**
|
|
||||||
* Define the crossorigin attribute setting of elements that provide CORS support.
|
|
||||||
*/
|
|
||||||
crossOrigin?: CrossOrigin;
|
|
||||||
/**
|
|
||||||
* Delete the output path before building.
|
|
||||||
*/
|
|
||||||
deleteOutputPath?: boolean;
|
|
||||||
/**
|
|
||||||
* URL where files will be deployed.
|
|
||||||
*/
|
|
||||||
deployUrl?: string;
|
|
||||||
/**
|
|
||||||
* Enables conditionally loaded ES2015 polyfills.
|
|
||||||
* @deprecated This will be determined from the list of supported browsers specified in the
|
|
||||||
* 'browserslist' file.
|
|
||||||
*/
|
|
||||||
es5BrowserSupport?: boolean;
|
|
||||||
/**
|
|
||||||
* Output in-file eval sourcemaps.
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
evalSourceMap?: boolean;
|
|
||||||
/**
|
|
||||||
* Concatenate modules with Rollup before bundling them with Webpack.
|
|
||||||
*/
|
|
||||||
experimentalRollupPass?: boolean;
|
|
||||||
/**
|
|
||||||
* Extract css from global styles into css files instead of js ones.
|
|
||||||
*/
|
|
||||||
extractCss?: boolean;
|
|
||||||
/**
|
|
||||||
* Extract all licenses in a separate file.
|
|
||||||
*/
|
|
||||||
extractLicenses?: boolean;
|
|
||||||
/**
|
|
||||||
* Replace files with other files in the build.
|
|
||||||
*/
|
|
||||||
fileReplacements?: FileReplacement[];
|
|
||||||
/**
|
|
||||||
* Run the TypeScript type checker in a forked process.
|
|
||||||
*/
|
|
||||||
forkTypeChecker?: boolean;
|
|
||||||
/**
|
|
||||||
* Localization file to use for i18n.
|
|
||||||
* @deprecated Use 'locales' object in the project metadata instead.
|
|
||||||
*/
|
|
||||||
i18nFile?: string;
|
|
||||||
/**
|
|
||||||
* Format of the localization file specified with --i18n-file.
|
|
||||||
* @deprecated No longer needed as the format will be determined automatically.
|
|
||||||
*/
|
|
||||||
i18nFormat?: string;
|
|
||||||
/**
|
|
||||||
* Locale to use for i18n.
|
|
||||||
* @deprecated Use 'localize' instead.
|
|
||||||
*/
|
|
||||||
i18nLocale?: string;
|
|
||||||
/**
|
|
||||||
* How to handle missing translations for i18n.
|
|
||||||
*/
|
|
||||||
i18nMissingTranslation?: I18NMissingTranslation;
|
|
||||||
/**
|
|
||||||
* Configures the generation of the application's HTML index.
|
|
||||||
*/
|
|
||||||
index: IndexUnion;
|
|
||||||
/**
|
|
||||||
* List of additional NgModule files that will be lazy loaded. Lazy router modules will be
|
|
||||||
* discovered automatically.
|
|
||||||
* @deprecated 'SystemJsNgModuleLoader' is deprecated, and this is part of its usage. Use
|
|
||||||
* 'import()' syntax instead.
|
|
||||||
*/
|
|
||||||
lazyModules?: string[];
|
|
||||||
localize?: Localize;
|
|
||||||
/**
|
|
||||||
* The full path for the main entry point to the app, relative to the current workspace.
|
|
||||||
*/
|
|
||||||
main: string;
|
|
||||||
/**
|
|
||||||
* Use file name for lazy loaded chunks.
|
|
||||||
*/
|
|
||||||
namedChunks?: boolean;
|
|
||||||
/**
|
|
||||||
* Path to ngsw-config.json.
|
|
||||||
*/
|
|
||||||
ngswConfigPath?: string;
|
|
||||||
/**
|
|
||||||
* Enables optimization of the build output.
|
|
||||||
*/
|
|
||||||
optimization?: OptimizationUnion;
|
|
||||||
/**
|
|
||||||
* Define the output filename cache-busting hashing mode.
|
|
||||||
*/
|
|
||||||
outputHashing?: OutputHashing;
|
|
||||||
/**
|
|
||||||
* The full path for the new output directory, relative to the current workspace.
|
|
||||||
*
|
|
||||||
* By default, writes output to a folder named dist/ in the current project.
|
|
||||||
*/
|
|
||||||
outputPath: string;
|
|
||||||
/**
|
|
||||||
* Enable and define the file watching poll time period in milliseconds.
|
|
||||||
*/
|
|
||||||
poll?: number;
|
|
||||||
/**
|
|
||||||
* The full path for the polyfills file, relative to the current workspace.
|
|
||||||
*/
|
|
||||||
polyfills?: string;
|
|
||||||
/**
|
|
||||||
* Do not use the real path when resolving modules.
|
|
||||||
*/
|
|
||||||
preserveSymlinks?: boolean;
|
|
||||||
/**
|
|
||||||
* Output profile events for Chrome profiler.
|
|
||||||
* @deprecated Use "NG_BUILD_PROFILING" environment variable instead.
|
|
||||||
*/
|
|
||||||
profile?: boolean;
|
|
||||||
/**
|
|
||||||
* Log progress to the console while building.
|
|
||||||
*/
|
|
||||||
progress?: boolean;
|
|
||||||
/**
|
|
||||||
* Change root relative URLs in stylesheets to include base HREF and deploy URL. Use only
|
|
||||||
* for compatibility and transition. The behavior of this option is non-standard and will be
|
|
||||||
* removed in the next major release.
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
rebaseRootRelativeCssUrls?: boolean;
|
|
||||||
/**
|
|
||||||
* The path where style resources will be placed, relative to outputPath.
|
|
||||||
*/
|
|
||||||
resourcesOutputPath?: string;
|
|
||||||
/**
|
|
||||||
* Global scripts to be included in the build.
|
|
||||||
*/
|
|
||||||
scripts?: ExtraEntryPoint[];
|
|
||||||
/**
|
|
||||||
* Generates a service worker config for production builds.
|
|
||||||
*/
|
|
||||||
serviceWorker?: boolean;
|
|
||||||
/**
|
|
||||||
* Flag to prevent building an app shell.
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
skipAppShell?: boolean;
|
|
||||||
/**
|
|
||||||
* Output sourcemaps.
|
|
||||||
*/
|
|
||||||
sourceMap?: SourceMapUnion;
|
|
||||||
/**
|
|
||||||
* Generates a 'stats.json' file which can be analyzed using tools such as
|
|
||||||
* 'webpack-bundle-analyzer'.
|
|
||||||
*/
|
|
||||||
statsJson?: boolean;
|
|
||||||
/**
|
|
||||||
* Options to pass to style preprocessors.
|
|
||||||
*/
|
|
||||||
stylePreprocessorOptions?: StylePreprocessorOptions;
|
|
||||||
/**
|
|
||||||
* Global styles to be included in the build.
|
|
||||||
*/
|
|
||||||
styles?: ExtraEntryPoint[];
|
|
||||||
/**
|
|
||||||
* Enables the use of subresource integrity validation.
|
|
||||||
*/
|
|
||||||
subresourceIntegrity?: boolean;
|
|
||||||
/**
|
|
||||||
* The full path for the TypeScript configuration file, relative to the current workspace.
|
|
||||||
*/
|
|
||||||
tsConfig: string;
|
|
||||||
/**
|
|
||||||
* Use a separate bundle containing only vendor libraries.
|
|
||||||
*/
|
|
||||||
vendorChunk?: boolean;
|
|
||||||
/**
|
|
||||||
* Resolve vendor packages sourcemaps.
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
vendorSourceMap?: boolean;
|
|
||||||
/**
|
|
||||||
* Adds more details to output logging.
|
|
||||||
*/
|
|
||||||
verbose?: boolean;
|
|
||||||
/**
|
|
||||||
* Run build when files change.
|
|
||||||
*/
|
|
||||||
watch?: boolean;
|
|
||||||
/**
|
|
||||||
* TypeScript configuration for Web Worker modules.
|
|
||||||
*/
|
|
||||||
webWorkerTsConfig?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AssetPattern = AssetPatternClass | string;
|
|
||||||
|
|
||||||
export interface AssetPatternClass {
|
|
||||||
/**
|
|
||||||
* The pattern to match.
|
|
||||||
*/
|
|
||||||
glob: string;
|
|
||||||
/**
|
|
||||||
* An array of globs to ignore.
|
|
||||||
*/
|
|
||||||
ignore?: string[];
|
|
||||||
/**
|
|
||||||
* The input directory path in which to apply 'glob'. Defaults to the project root.
|
|
||||||
*/
|
|
||||||
input: string;
|
|
||||||
/**
|
|
||||||
* Absolute path within the output.
|
|
||||||
*/
|
|
||||||
output: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Budget {
|
|
||||||
/**
|
|
||||||
* The baseline size for comparison.
|
|
||||||
*/
|
|
||||||
baseline?: string;
|
|
||||||
/**
|
|
||||||
* The threshold for error relative to the baseline (min & max).
|
|
||||||
*/
|
|
||||||
error?: string;
|
|
||||||
/**
|
|
||||||
* The maximum threshold for error relative to the baseline.
|
|
||||||
*/
|
|
||||||
maximumError?: string;
|
|
||||||
/**
|
|
||||||
* The maximum threshold for warning relative to the baseline.
|
|
||||||
*/
|
|
||||||
maximumWarning?: string;
|
|
||||||
/**
|
|
||||||
* The minimum threshold for error relative to the baseline.
|
|
||||||
*/
|
|
||||||
minimumError?: string;
|
|
||||||
/**
|
|
||||||
* The minimum threshold for warning relative to the baseline.
|
|
||||||
*/
|
|
||||||
minimumWarning?: string;
|
|
||||||
/**
|
|
||||||
* The name of the bundle.
|
|
||||||
*/
|
|
||||||
name?: string;
|
|
||||||
/**
|
|
||||||
* The type of budget.
|
|
||||||
*/
|
|
||||||
type: Type;
|
|
||||||
/**
|
|
||||||
* The threshold for warning relative to the baseline (min & max).
|
|
||||||
*/
|
|
||||||
warning?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of budget.
|
|
||||||
*/
|
|
||||||
export enum Type {
|
|
||||||
All = 'all',
|
|
||||||
AllScript = 'allScript',
|
|
||||||
Any = 'any',
|
|
||||||
AnyComponentStyle = 'anyComponentStyle',
|
|
||||||
AnyScript = 'anyScript',
|
|
||||||
Bundle = 'bundle',
|
|
||||||
Initial = 'initial',
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the crossorigin attribute setting of elements that provide CORS support.
|
|
||||||
*/
|
|
||||||
export enum CrossOrigin {
|
|
||||||
Anonymous = 'anonymous',
|
|
||||||
None = 'none',
|
|
||||||
UseCredentials = 'use-credentials',
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FileReplacement {
|
|
||||||
replace?: string;
|
|
||||||
replaceWith?: string;
|
|
||||||
src?: string;
|
|
||||||
with?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* How to handle missing translations for i18n.
|
|
||||||
*/
|
|
||||||
export enum I18NMissingTranslation {
|
|
||||||
Error = 'error',
|
|
||||||
Ignore = 'ignore',
|
|
||||||
Warning = 'warning',
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures the generation of the application's HTML index.
|
|
||||||
*/
|
|
||||||
export type IndexUnion = IndexObject | string;
|
|
||||||
|
|
||||||
export interface IndexObject {
|
|
||||||
/**
|
|
||||||
* The path of a file to use for the application's generated HTML index.
|
|
||||||
*/
|
|
||||||
input: string;
|
|
||||||
/**
|
|
||||||
* The output path of the application's generated HTML index file. The full provided path
|
|
||||||
* will be used and will be considered relative to the application's configured output path.
|
|
||||||
*/
|
|
||||||
output?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Localize = string[] | boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables optimization of the build output.
|
|
||||||
*/
|
|
||||||
export type OptimizationUnion = boolean | OptimizationClass;
|
|
||||||
|
|
||||||
export interface OptimizationClass {
|
|
||||||
/**
|
|
||||||
* Enables optimization of the scripts output.
|
|
||||||
*/
|
|
||||||
scripts?: boolean;
|
|
||||||
/**
|
|
||||||
* Enables optimization of the styles output.
|
|
||||||
*/
|
|
||||||
styles?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the output filename cache-busting hashing mode.
|
|
||||||
*/
|
|
||||||
export enum OutputHashing {
|
|
||||||
All = 'all',
|
|
||||||
Bundles = 'bundles',
|
|
||||||
Media = 'media',
|
|
||||||
None = 'none',
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ExtraEntryPoint = ExtraEntryPointClass | string;
|
|
||||||
|
|
||||||
export interface ExtraEntryPointClass {
|
|
||||||
/**
|
|
||||||
* The bundle name for this extra entry point.
|
|
||||||
*/
|
|
||||||
bundleName?: string;
|
|
||||||
/**
|
|
||||||
* If the bundle will be referenced in the HTML file.
|
|
||||||
*/
|
|
||||||
inject?: boolean;
|
|
||||||
/**
|
|
||||||
* The file to include.
|
|
||||||
*/
|
|
||||||
input: string;
|
|
||||||
/**
|
|
||||||
* If the bundle will be lazy loaded.
|
|
||||||
*/
|
|
||||||
lazy?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Output sourcemaps.
|
|
||||||
*/
|
|
||||||
export type SourceMapUnion = boolean | SourceMapClass;
|
|
||||||
|
|
||||||
export interface SourceMapClass {
|
|
||||||
/**
|
|
||||||
* Output sourcemaps used for error reporting tools.
|
|
||||||
*/
|
|
||||||
hidden?: boolean;
|
|
||||||
/**
|
|
||||||
* Output sourcemaps for all scripts.
|
|
||||||
*/
|
|
||||||
scripts?: boolean;
|
|
||||||
/**
|
|
||||||
* Output sourcemaps for all styles.
|
|
||||||
*/
|
|
||||||
styles?: boolean;
|
|
||||||
/**
|
|
||||||
* Resolve vendor packages sourcemaps.
|
|
||||||
*/
|
|
||||||
vendor?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Options to pass to style preprocessors.
|
|
||||||
*/
|
|
||||||
export interface StylePreprocessorOptions {
|
|
||||||
/**
|
|
||||||
* Paths to include. Paths will be resolved to project root.
|
|
||||||
*/
|
|
||||||
includePaths?: string[];
|
|
||||||
}
|
|
||||||
@ -1,96 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
// TODO: cleanup this file, it's copied as is from Angular CLI.
|
|
||||||
|
|
||||||
import { ScriptTarget } from 'typescript';
|
|
||||||
import {
|
|
||||||
AssetPatternClass,
|
|
||||||
Budget,
|
|
||||||
ExtraEntryPoint,
|
|
||||||
OptimizationClass,
|
|
||||||
SourceMapClass,
|
|
||||||
} from '../../browser/schema';
|
|
||||||
import { NormalizedFileReplacement } from '../../utils/normalize-file-replacements';
|
|
||||||
|
|
||||||
export interface BuildOptions {
|
|
||||||
optimization: OptimizationClass;
|
|
||||||
environment?: string;
|
|
||||||
outputPath: string;
|
|
||||||
resourcesOutputPath?: string;
|
|
||||||
aot?: boolean;
|
|
||||||
sourceMap: SourceMapClass;
|
|
||||||
/** @deprecated use sourceMap instead */
|
|
||||||
vendorSourceMap?: boolean;
|
|
||||||
/** @deprecated */
|
|
||||||
evalSourceMap?: boolean;
|
|
||||||
vendorChunk?: boolean;
|
|
||||||
commonChunk?: boolean;
|
|
||||||
runtimeChunk?: boolean;
|
|
||||||
baseHref?: string;
|
|
||||||
deployUrl?: string;
|
|
||||||
verbose?: boolean;
|
|
||||||
progress?: boolean;
|
|
||||||
i18nFile?: string;
|
|
||||||
i18nFormat?: string;
|
|
||||||
i18nLocale?: string;
|
|
||||||
i18nMissingTranslation?: string;
|
|
||||||
extractCss?: boolean;
|
|
||||||
bundleDependencies?: 'none' | 'all';
|
|
||||||
watch?: boolean;
|
|
||||||
outputHashing?: string;
|
|
||||||
poll?: number;
|
|
||||||
app?: string;
|
|
||||||
deleteOutputPath?: boolean;
|
|
||||||
preserveSymlinks?: boolean;
|
|
||||||
extractLicenses?: boolean;
|
|
||||||
buildOptimizer?: boolean;
|
|
||||||
namedChunks?: boolean;
|
|
||||||
subresourceIntegrity?: boolean;
|
|
||||||
serviceWorker?: boolean;
|
|
||||||
webWorkerTsConfig?: string;
|
|
||||||
skipAppShell?: boolean;
|
|
||||||
statsJson: boolean;
|
|
||||||
forkTypeChecker: boolean;
|
|
||||||
profile?: boolean;
|
|
||||||
es5BrowserSupport?: boolean;
|
|
||||||
|
|
||||||
main: string;
|
|
||||||
polyfills?: string;
|
|
||||||
budgets: Budget[];
|
|
||||||
assets: AssetPatternClass[];
|
|
||||||
scripts: ExtraEntryPoint[];
|
|
||||||
styles: ExtraEntryPoint[];
|
|
||||||
stylePreprocessorOptions?: { includePaths: string[] };
|
|
||||||
lazyModules: string[];
|
|
||||||
platform?: 'browser' | 'server';
|
|
||||||
fileReplacements: NormalizedFileReplacement[];
|
|
||||||
/** @deprecated use only for compatibility in 8.x; will be removed in 9.0 */
|
|
||||||
rebaseRootRelativeCssUrls?: boolean;
|
|
||||||
|
|
||||||
/* Append script target version to filename. */
|
|
||||||
esVersionInFileName?: boolean;
|
|
||||||
|
|
||||||
/* When specified it will be used instead of the script target in the tsconfig.json. */
|
|
||||||
scriptTargetOverride?: ScriptTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WebpackTestOptions extends BuildOptions {
|
|
||||||
codeCoverage?: boolean;
|
|
||||||
codeCoverageExclude?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WebpackConfigOptions<T = BuildOptions> {
|
|
||||||
root: string;
|
|
||||||
projectRoot: string;
|
|
||||||
sourceRoot?: string;
|
|
||||||
buildOptions: T;
|
|
||||||
tsConfig: any;
|
|
||||||
tsConfigPath: string;
|
|
||||||
supportES2015: boolean;
|
|
||||||
}
|
|
||||||
@ -1,94 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
// tslint:disable
|
|
||||||
// TODO: cleanup this file, it's copied as is from Angular CLI.
|
|
||||||
|
|
||||||
import { basename } from 'path';
|
|
||||||
import { normalizePath } from '@nrwl/devkit';
|
|
||||||
import { ExtraEntryPoint, ExtraEntryPointClass } from '../../../browser/schema';
|
|
||||||
import { ScriptTarget } from 'typescript';
|
|
||||||
|
|
||||||
export interface HashFormat {
|
|
||||||
chunk: string;
|
|
||||||
extract: string;
|
|
||||||
file: string;
|
|
||||||
script: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getOutputHashFormat(option: string, length = 20): HashFormat {
|
|
||||||
const hashFormats: { [option: string]: HashFormat } = {
|
|
||||||
none: { chunk: '', extract: '', file: '', script: '' },
|
|
||||||
media: { chunk: '', extract: '', file: `.[hash:${length}]`, script: '' },
|
|
||||||
bundles: {
|
|
||||||
chunk: `.[chunkhash:${length}]`,
|
|
||||||
extract: `.[contenthash:${length}]`,
|
|
||||||
file: '',
|
|
||||||
script: `.[hash:${length}]`,
|
|
||||||
},
|
|
||||||
all: {
|
|
||||||
chunk: `.[chunkhash:${length}]`,
|
|
||||||
extract: `.[contenthash:${length}]`,
|
|
||||||
file: `.[hash:${length}]`,
|
|
||||||
script: `.[hash:${length}]`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return hashFormats[option] || hashFormats['none'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: replace with Omit when we update to TS 3.5
|
|
||||||
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
|
||||||
export type NormalizedEntryPoint = Required<Omit<ExtraEntryPointClass, 'lazy'>>;
|
|
||||||
|
|
||||||
export function normalizeExtraEntryPoints(
|
|
||||||
extraEntryPoints: ExtraEntryPoint[],
|
|
||||||
defaultBundleName: string
|
|
||||||
): NormalizedEntryPoint[] {
|
|
||||||
return extraEntryPoints.map((entry) => {
|
|
||||||
let normalizedEntry;
|
|
||||||
if (typeof entry === 'string') {
|
|
||||||
normalizedEntry = {
|
|
||||||
input: entry,
|
|
||||||
inject: true,
|
|
||||||
bundleName: defaultBundleName,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
const { lazy, inject = true, ...newEntry } = entry;
|
|
||||||
const injectNormalized = entry.lazy !== undefined ? !entry.lazy : inject;
|
|
||||||
let bundleName;
|
|
||||||
|
|
||||||
if (entry.bundleName) {
|
|
||||||
bundleName = entry.bundleName;
|
|
||||||
} else if (!injectNormalized) {
|
|
||||||
// Lazy entry points use the file name as bundle name.
|
|
||||||
bundleName = basename(
|
|
||||||
normalizePath(
|
|
||||||
entry.input.replace(/\.(js|css|scss|sass|less|styl)$/i, '')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
bundleName = defaultBundleName;
|
|
||||||
}
|
|
||||||
|
|
||||||
normalizedEntry = { ...newEntry, inject: injectNormalized, bundleName };
|
|
||||||
}
|
|
||||||
|
|
||||||
return normalizedEntry;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an ES version file suffix to differentiate between various builds.
|
|
||||||
*/
|
|
||||||
export function getEsVersionForFileName(
|
|
||||||
scriptTargetOverride: ScriptTarget | undefined,
|
|
||||||
esVersionInFileName = false
|
|
||||||
): string {
|
|
||||||
return scriptTargetOverride && esVersionInFileName
|
|
||||||
? `-${ScriptTarget[scriptTargetOverride].toLowerCase()}`
|
|
||||||
: '';
|
|
||||||
}
|
|
||||||
@ -1,142 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google LLC All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
import * as webpack from 'webpack';
|
|
||||||
import { basename, dirname, extname } from 'path';
|
|
||||||
import { FileInfo } from '../utils/index-file/augment-index-html';
|
|
||||||
import {
|
|
||||||
IndexHtmlGenerator,
|
|
||||||
IndexHtmlGeneratorOptions,
|
|
||||||
IndexHtmlGeneratorProcessOptions,
|
|
||||||
} from '../utils/index-file/index-html-generator';
|
|
||||||
|
|
||||||
export interface IndexHtmlWebpackPluginOptions
|
|
||||||
extends IndexHtmlGeneratorOptions,
|
|
||||||
Omit<
|
|
||||||
IndexHtmlGeneratorProcessOptions,
|
|
||||||
'files' | 'noModuleFiles' | 'moduleFiles'
|
|
||||||
> {
|
|
||||||
noModuleEntrypoints: string[];
|
|
||||||
moduleEntrypoints: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
type Compiler = any;
|
|
||||||
|
|
||||||
const PLUGIN_NAME = 'index-html-webpack-plugin';
|
|
||||||
|
|
||||||
export class IndexHtmlWebpackPlugin extends IndexHtmlGenerator {
|
|
||||||
private _compilation: any | undefined;
|
|
||||||
|
|
||||||
get compilation(): any {
|
|
||||||
if (this._compilation) {
|
|
||||||
return this._compilation;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('compilation is undefined.');
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(readonly options: IndexHtmlWebpackPluginOptions) {
|
|
||||||
super(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
apply(compiler: Compiler) {
|
|
||||||
compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
|
|
||||||
this._compilation = compilation;
|
|
||||||
compilation.hooks.processAssets.tapPromise(
|
|
||||||
{
|
|
||||||
name: PLUGIN_NAME,
|
|
||||||
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE + 1,
|
|
||||||
},
|
|
||||||
callback
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
function addWarning(compilation: any, message: string): void {
|
|
||||||
compilation.warnings.push(new this.webpack.WebpackError(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
function addError(compilation: any, message: string): void {
|
|
||||||
compilation.errors.push(new this.webpack.WebpackError(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
const callback = async (assets: Record<string, unknown>) => {
|
|
||||||
// Get all files for selected entrypoints
|
|
||||||
const files: FileInfo[] = [];
|
|
||||||
const noModuleFiles: FileInfo[] = [];
|
|
||||||
const moduleFiles: FileInfo[] = [];
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (const [entryName, entrypoint] of this.compilation.entrypoints) {
|
|
||||||
const entryFiles: FileInfo[] = entrypoint
|
|
||||||
?.getFiles()
|
|
||||||
?.filter((f) => !f.endsWith('.hot-update.js'))
|
|
||||||
?.map(
|
|
||||||
(f: string): FileInfo => ({
|
|
||||||
name: entryName,
|
|
||||||
file: f,
|
|
||||||
extension: extname(f),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!entryFiles) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.noModuleEntrypoints.includes(entryName)) {
|
|
||||||
noModuleFiles.push(...entryFiles);
|
|
||||||
} else if (this.options.moduleEntrypoints.includes(entryName)) {
|
|
||||||
moduleFiles.push(...entryFiles);
|
|
||||||
} else {
|
|
||||||
files.push(...entryFiles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { content, warnings, errors } = await this.process({
|
|
||||||
files,
|
|
||||||
noModuleFiles,
|
|
||||||
moduleFiles,
|
|
||||||
outputPath: dirname(this.options.outputPath),
|
|
||||||
baseHref: this.options.baseHref,
|
|
||||||
lang: this.options.lang,
|
|
||||||
});
|
|
||||||
|
|
||||||
assets[this.options.outputPath] = new webpack.sources.RawSource(
|
|
||||||
content
|
|
||||||
);
|
|
||||||
|
|
||||||
warnings.forEach((msg) => addWarning(this.compilation, msg));
|
|
||||||
errors.forEach((msg) => addError(this.compilation, msg));
|
|
||||||
} catch (error) {
|
|
||||||
addError(this.compilation, error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async readAsset(path: string): Promise<string> {
|
|
||||||
const data = this.compilation.assets[basename(path)].source();
|
|
||||||
|
|
||||||
return typeof data === 'string' ? data : data.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async readIndex(path: string): Promise<string> {
|
|
||||||
return new Promise<string>((resolve, reject) => {
|
|
||||||
this.compilation.inputFileSystem.readFile(
|
|
||||||
path,
|
|
||||||
(err?: Error, data?: string | Buffer) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.compilation.fileDependencies.add(path);
|
|
||||||
resolve(data?.toString() ?? '');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
import { Compiler } from 'webpack';
|
|
||||||
|
|
||||||
export class NamedLazyChunksPlugin {
|
|
||||||
constructor() {}
|
|
||||||
apply(compiler: Compiler): void {
|
|
||||||
// Webpack doesn't export these so the deep imports can potentially break.
|
|
||||||
// There doesn't seem to exist any ergonomic way to alter chunk names for non-context lazy chunks
|
|
||||||
// (https://github.com/webpack/webpack/issues/9075) so this is the best alternative for now.
|
|
||||||
const ImportDependency = require('webpack/lib/dependencies/ImportDependency');
|
|
||||||
const ImportDependenciesBlock = require('webpack/lib/dependencies/ImportDependenciesBlock');
|
|
||||||
const Template = require('webpack/lib/Template');
|
|
||||||
compiler.hooks.compilation.tap(
|
|
||||||
'named-lazy-chunks-plugin',
|
|
||||||
(compilation) => {
|
|
||||||
// The dependencyReference hook isn't in the webpack typings so we have to type it as any.
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
(compilation.hooks as any).dependencyReference.tap(
|
|
||||||
'named-lazy-chunks-plugin',
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
(_: any, dependency: any) => {
|
|
||||||
if (
|
|
||||||
// Check this dependency is from an `import()` statement.
|
|
||||||
dependency instanceof ImportDependency &&
|
|
||||||
dependency.block instanceof ImportDependenciesBlock &&
|
|
||||||
// Don't rename chunks that already have a name.
|
|
||||||
dependency.block.chunkName === null
|
|
||||||
) {
|
|
||||||
// Convert the request to a valid chunk name using the same logic used
|
|
||||||
// in webpack/lib/ContextModule.js
|
|
||||||
dependency.block.chunkName = Template.toPath(dependency.request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default function (content: string, map: object) {
|
|
||||||
const stringifiedContent = JSON.stringify(content);
|
|
||||||
const stringifiedMap = map ? JSON.stringify(map) : `''`;
|
|
||||||
|
|
||||||
return `module.exports = [[module.id, ${stringifiedContent}, '', ${stringifiedMap}]]`;
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Exports the webpack plugins we use internally.
|
|
||||||
export {
|
|
||||||
ScriptsWebpackPlugin,
|
|
||||||
ScriptsWebpackPluginOptions,
|
|
||||||
} from './scripts-webpack-plugin';
|
|
||||||
export {
|
|
||||||
RemoveHashPlugin,
|
|
||||||
RemoveHashPluginOptions,
|
|
||||||
} from './remove-hash-plugin';
|
|
||||||
export {
|
|
||||||
default as PostcssCliResources,
|
|
||||||
PostcssCliResourcesOptions,
|
|
||||||
} from './postcss-cli-resources';
|
|
||||||
|
|
||||||
import { join } from 'path';
|
|
||||||
export const RawCssLoader = require.resolve(join(__dirname, 'raw-css-loader'));
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`augment-index-html can generate index.html 1`] = `"<html><head><base href=\\"/\\"><link rel=\\"stylesheet\\" href=\\"styles.css\\"></head><body><script src=\\"runtime.js\\" defer></script><script src=\\"polyfills.js\\" defer></script><script src=\\"main.js\\" defer></script></body></html>"`;
|
|
||||||
|
|
||||||
exports[`augment-index-html should emit correct script tags when having 'module' and 'non-module' js 1`] = `"<html><head><base href=\\"/\\"><link rel=\\"stylesheet\\" href=\\"styles.css\\"></head><body><script src=\\"runtime-es2015.js\\" type=\\"module\\"></script><script src=\\"polyfills-es2015.js\\" type=\\"module\\"></script><script src=\\"runtime-es5.js\\" nomodule defer></script><script src=\\"polyfills-es5.js\\" nomodule defer></script><script src=\\"main-es2015.js\\" type=\\"module\\"></script><script src=\\"main-es5.js\\" nomodule defer></script></body></html>"`;
|
|
||||||
|
|
||||||
exports[`augment-index-html should not add 'module' and 'non-module' attr to js files which are in both module formats 1`] = `"<html><head><base href=\\"/\\"><link rel=\\"stylesheet\\" href=\\"styles.css\\"></head><body><script src=\\"scripts.js\\" defer></script><script src=\\"main-es2015.js\\" type=\\"module\\"></script><script src=\\"main-es5.js\\" nomodule defer></script></body></html>"`;
|
|
||||||
@ -1,94 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
AugmentIndexHtmlOptions,
|
|
||||||
FileInfo,
|
|
||||||
augmentIndexHtml,
|
|
||||||
} from './augment-index-html';
|
|
||||||
|
|
||||||
describe('augment-index-html', () => {
|
|
||||||
const indexGeneratorOptions: AugmentIndexHtmlOptions = {
|
|
||||||
input: 'index.html',
|
|
||||||
inputContent: '<html><head></head><body></body></html>',
|
|
||||||
baseHref: '/',
|
|
||||||
sri: false,
|
|
||||||
files: [],
|
|
||||||
loadOutputFile: (_fileName: string) => '',
|
|
||||||
entrypoints: ['scripts', 'polyfills', 'main', 'styles'],
|
|
||||||
};
|
|
||||||
|
|
||||||
it('can generate index.html', async () => {
|
|
||||||
const source = augmentIndexHtml({
|
|
||||||
...indexGeneratorOptions,
|
|
||||||
files: [
|
|
||||||
{ file: 'styles.css', extension: '.css', name: 'styles' },
|
|
||||||
{ file: 'runtime.js', extension: '.js', name: 'main' },
|
|
||||||
{ file: 'main.js', extension: '.js', name: 'main' },
|
|
||||||
{ file: 'runtime.js', extension: '.js', name: 'polyfills' },
|
|
||||||
{ file: 'polyfills.js', extension: '.js', name: 'polyfills' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
const html = await source;
|
|
||||||
expect(html).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should emit correct script tags when having 'module' and 'non-module' js`, async () => {
|
|
||||||
const es2015JsFiles: FileInfo[] = [
|
|
||||||
{ file: 'runtime-es2015.js', extension: '.js', name: 'main' },
|
|
||||||
{ file: 'main-es2015.js', extension: '.js', name: 'main' },
|
|
||||||
{ file: 'runtime-es2015.js', extension: '.js', name: 'polyfills' },
|
|
||||||
{ file: 'polyfills-es2015.js', extension: '.js', name: 'polyfills' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const es5JsFiles: FileInfo[] = [
|
|
||||||
{ file: 'runtime-es5.js', extension: '.js', name: 'main' },
|
|
||||||
{ file: 'main-es5.js', extension: '.js', name: 'main' },
|
|
||||||
{ file: 'runtime-es5.js', extension: '.js', name: 'polyfills' },
|
|
||||||
{ file: 'polyfills-es5.js', extension: '.js', name: 'polyfills' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const source = augmentIndexHtml({
|
|
||||||
...indexGeneratorOptions,
|
|
||||||
files: [
|
|
||||||
{ file: 'styles.css', extension: '.css', name: 'styles' },
|
|
||||||
{ file: 'styles.css', extension: '.css', name: 'styles' },
|
|
||||||
],
|
|
||||||
moduleFiles: es2015JsFiles,
|
|
||||||
noModuleFiles: es5JsFiles,
|
|
||||||
});
|
|
||||||
|
|
||||||
const html = await source;
|
|
||||||
expect(html).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should not add 'module' and 'non-module' attr to js files which are in both module formats`, async () => {
|
|
||||||
const es2015JsFiles: FileInfo[] = [
|
|
||||||
{ file: 'scripts.js', extension: '.js', name: 'scripts' },
|
|
||||||
{ file: 'main-es2015.js', extension: '.js', name: 'main' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const es5JsFiles: FileInfo[] = [
|
|
||||||
{ file: 'scripts.js', extension: '.js', name: 'scripts' },
|
|
||||||
{ file: 'main-es5.js', extension: '.js', name: 'main' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const source = augmentIndexHtml({
|
|
||||||
...indexGeneratorOptions,
|
|
||||||
files: [
|
|
||||||
{ file: 'styles.css', extension: '.css', name: 'styles' },
|
|
||||||
{ file: 'styles.css', extension: '.css', name: 'styles' },
|
|
||||||
],
|
|
||||||
moduleFiles: es2015JsFiles,
|
|
||||||
noModuleFiles: es5JsFiles,
|
|
||||||
});
|
|
||||||
|
|
||||||
const html = await source;
|
|
||||||
expect(html).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,94 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { dirname, join } from 'path';
|
|
||||||
import { ExtraEntryPoint } from '../../../browser/schema';
|
|
||||||
import { generateEntryPoints } from '../package-chunk-sort';
|
|
||||||
import { stripBom } from '../strip-bom';
|
|
||||||
import {
|
|
||||||
augmentIndexHtml,
|
|
||||||
CrossOriginValue,
|
|
||||||
FileInfo,
|
|
||||||
} from './augment-index-html';
|
|
||||||
import { readFileSync, writeFileSync } from 'fs';
|
|
||||||
import { interpolateEnvironmentVariablesToIndex } from '../../../../interpolate-env-variables-to-index';
|
|
||||||
import { EmittedFile } from '../../../../run-webpack';
|
|
||||||
|
|
||||||
type ExtensionFilter = '.js' | '.css';
|
|
||||||
|
|
||||||
export interface WriteIndexHtmlOptions {
|
|
||||||
outputPath: string;
|
|
||||||
indexPath: string;
|
|
||||||
files?: EmittedFile[];
|
|
||||||
noModuleFiles?: EmittedFile[];
|
|
||||||
moduleFiles?: EmittedFile[];
|
|
||||||
baseHref?: string;
|
|
||||||
deployUrl?: string;
|
|
||||||
sri?: boolean;
|
|
||||||
scripts?: ExtraEntryPoint[];
|
|
||||||
styles?: ExtraEntryPoint[];
|
|
||||||
postTransform?: IndexHtmlTransform;
|
|
||||||
crossOrigin?: CrossOriginValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type IndexHtmlTransform = (content: string) => Promise<string>;
|
|
||||||
|
|
||||||
export async function writeIndexHtml({
|
|
||||||
outputPath,
|
|
||||||
indexPath,
|
|
||||||
files = [],
|
|
||||||
noModuleFiles = [],
|
|
||||||
moduleFiles = [],
|
|
||||||
baseHref,
|
|
||||||
deployUrl,
|
|
||||||
sri = false,
|
|
||||||
scripts = [],
|
|
||||||
styles = [],
|
|
||||||
postTransform,
|
|
||||||
crossOrigin,
|
|
||||||
}: WriteIndexHtmlOptions) {
|
|
||||||
let content = readFileSync(indexPath).toString();
|
|
||||||
content = stripBom(content);
|
|
||||||
content = augmentIndexHtml({
|
|
||||||
input: outputPath,
|
|
||||||
inputContent: interpolateEnvironmentVariablesToIndex(content, deployUrl),
|
|
||||||
baseHref,
|
|
||||||
deployUrl,
|
|
||||||
crossOrigin,
|
|
||||||
sri,
|
|
||||||
entrypoints: generateEntryPoints({ scripts, styles }),
|
|
||||||
files: filterAndMapBuildFiles(files, ['.js', '.css']),
|
|
||||||
noModuleFiles: filterAndMapBuildFiles(noModuleFiles, '.js'),
|
|
||||||
moduleFiles: filterAndMapBuildFiles(moduleFiles, '.js'),
|
|
||||||
loadOutputFile: (filePath) =>
|
|
||||||
readFileSync(join(dirname(outputPath), filePath)).toString(),
|
|
||||||
});
|
|
||||||
if (postTransform) {
|
|
||||||
content = await postTransform(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeFileSync(outputPath, content);
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterAndMapBuildFiles(
|
|
||||||
files: EmittedFile[],
|
|
||||||
extensionFilter: ExtensionFilter | ExtensionFilter[]
|
|
||||||
): FileInfo[] {
|
|
||||||
const filteredFiles: FileInfo[] = [];
|
|
||||||
const validExtensions: string[] = Array.isArray(extensionFilter)
|
|
||||||
? extensionFilter
|
|
||||||
: [extensionFilter];
|
|
||||||
|
|
||||||
for (const { file, name, extension, initial } of files) {
|
|
||||||
if (name && initial && validExtensions.includes(extension)) {
|
|
||||||
filteredFiles.push({ file, extension, name });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return filteredFiles;
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
// tslint:disable
|
|
||||||
// TODO: cleanup this file, it's copied as is from Angular CLI.
|
|
||||||
|
|
||||||
import { statSync } from 'fs';
|
|
||||||
|
|
||||||
export function isDirectory(path: string) {
|
|
||||||
try {
|
|
||||||
return statSync(path).isDirectory();
|
|
||||||
} catch (_) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
// tslint:disable
|
|
||||||
// TODO: cleanup this file, it's copied as is from Angular CLI.
|
|
||||||
|
|
||||||
// Strip BOM from file data.
|
|
||||||
// https://stackoverflow.com/questions/24356713
|
|
||||||
export function stripBom(data: string) {
|
|
||||||
return data.replace(/^\uFEFF/, '');
|
|
||||||
}
|
|
||||||
@ -1,239 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google LLC All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
import { createHash } from 'crypto';
|
|
||||||
import { htmlRewritingStream } from './html-rewriting-stream';
|
|
||||||
|
|
||||||
export type LoadOutputFileFunctionType = (file: string) => Promise<string>;
|
|
||||||
|
|
||||||
export type CrossOriginValue = 'none' | 'anonymous' | 'use-credentials';
|
|
||||||
|
|
||||||
export interface AugmentIndexHtmlOptions {
|
|
||||||
/* Input contents */
|
|
||||||
html: string;
|
|
||||||
baseHref?: string;
|
|
||||||
deployUrl?: string;
|
|
||||||
sri: boolean;
|
|
||||||
/** crossorigin attribute setting of elements that provide CORS support */
|
|
||||||
crossOrigin?: CrossOriginValue;
|
|
||||||
/*
|
|
||||||
* Files emitted by the build.
|
|
||||||
* Js files will be added without 'nomodule' nor 'module'.
|
|
||||||
*/
|
|
||||||
files: FileInfo[];
|
|
||||||
/** Files that should be added using 'nomodule'. */
|
|
||||||
noModuleFiles?: FileInfo[];
|
|
||||||
/** Files that should be added using 'module'. */
|
|
||||||
moduleFiles?: FileInfo[];
|
|
||||||
/*
|
|
||||||
* Function that loads a file used.
|
|
||||||
* This allows us to use different routines within the IndexHtmlWebpackPlugin and
|
|
||||||
* when used without this plugin.
|
|
||||||
*/
|
|
||||||
loadOutputFile: LoadOutputFileFunctionType;
|
|
||||||
/** Used to sort the inseration of files in the HTML file */
|
|
||||||
entrypoints: string[];
|
|
||||||
/** Used to set the document default locale */
|
|
||||||
lang?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FileInfo {
|
|
||||||
file: string;
|
|
||||||
name: string;
|
|
||||||
extension: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Helper function used by the IndexHtmlWebpackPlugin.
|
|
||||||
* Can also be directly used by builder, e. g. in order to generate an index.html
|
|
||||||
* after processing several configurations in order to build different sets of
|
|
||||||
* bundles for differential serving.
|
|
||||||
*/
|
|
||||||
export async function augmentIndexHtml(
|
|
||||||
params: AugmentIndexHtmlOptions
|
|
||||||
): Promise<string> {
|
|
||||||
const {
|
|
||||||
loadOutputFile,
|
|
||||||
files,
|
|
||||||
noModuleFiles = [],
|
|
||||||
moduleFiles = [],
|
|
||||||
entrypoints,
|
|
||||||
sri,
|
|
||||||
deployUrl = '',
|
|
||||||
lang,
|
|
||||||
baseHref,
|
|
||||||
html,
|
|
||||||
} = params;
|
|
||||||
|
|
||||||
let { crossOrigin = 'none' } = params;
|
|
||||||
if (sri && crossOrigin === 'none') {
|
|
||||||
crossOrigin = 'anonymous';
|
|
||||||
}
|
|
||||||
|
|
||||||
const stylesheets = new Set<string>();
|
|
||||||
const scripts = new Set<string>();
|
|
||||||
|
|
||||||
// Sort files in the order we want to insert them by entrypoint and dedupes duplicates
|
|
||||||
const mergedFiles = [...moduleFiles, ...noModuleFiles, ...files];
|
|
||||||
for (const entrypoint of entrypoints) {
|
|
||||||
for (const { extension, file, name } of mergedFiles) {
|
|
||||||
if (name !== entrypoint) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (extension) {
|
|
||||||
case '.js':
|
|
||||||
scripts.add(file);
|
|
||||||
break;
|
|
||||||
case '.css':
|
|
||||||
stylesheets.add(file);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let scriptTags: string[] = [];
|
|
||||||
for (const script of scripts) {
|
|
||||||
const attrs = [`src="${deployUrl}${script}"`];
|
|
||||||
|
|
||||||
if (crossOrigin !== 'none') {
|
|
||||||
attrs.push(`crossorigin="${crossOrigin}"`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We want to include nomodule or module when a file is not common amongs all
|
|
||||||
// such as runtime.js
|
|
||||||
const scriptPredictor = ({ file }: FileInfo): boolean => file === script;
|
|
||||||
if (!files.some(scriptPredictor)) {
|
|
||||||
// in some cases for differential loading file with the same name is available in both
|
|
||||||
// nomodule and module such as scripts.js
|
|
||||||
// we shall not add these attributes if that's the case
|
|
||||||
const isNoModuleType = noModuleFiles.some(scriptPredictor);
|
|
||||||
const isModuleType = moduleFiles.some(scriptPredictor);
|
|
||||||
|
|
||||||
if (isNoModuleType && !isModuleType) {
|
|
||||||
attrs.push('nomodule', 'defer');
|
|
||||||
} else if (isModuleType && !isNoModuleType) {
|
|
||||||
attrs.push('type="module"');
|
|
||||||
} else {
|
|
||||||
attrs.push('defer');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
attrs.push('defer');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sri) {
|
|
||||||
const content = await loadOutputFile(script);
|
|
||||||
attrs.push(generateSriAttributes(content));
|
|
||||||
}
|
|
||||||
|
|
||||||
scriptTags.push(`<script ${attrs.join(' ')}></script>`);
|
|
||||||
}
|
|
||||||
|
|
||||||
let linkTags: string[] = [];
|
|
||||||
for (const stylesheet of stylesheets) {
|
|
||||||
const attrs = [`rel="stylesheet"`, `href="${deployUrl}${stylesheet}"`];
|
|
||||||
|
|
||||||
if (crossOrigin !== 'none') {
|
|
||||||
attrs.push(`crossorigin="${crossOrigin}"`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sri) {
|
|
||||||
const content = await loadOutputFile(stylesheet);
|
|
||||||
attrs.push(generateSriAttributes(content));
|
|
||||||
}
|
|
||||||
|
|
||||||
linkTags.push(`<link ${attrs.join(' ')}>`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { rewriter, transformedContent } = await htmlRewritingStream(html);
|
|
||||||
const baseTagExists = html.includes('<base');
|
|
||||||
|
|
||||||
rewriter
|
|
||||||
.on('startTag', (tag) => {
|
|
||||||
switch (tag.tagName) {
|
|
||||||
case 'html':
|
|
||||||
// Adjust document locale if specified
|
|
||||||
if (isString(lang)) {
|
|
||||||
updateAttribute(tag, 'lang', lang);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'head':
|
|
||||||
// Base href should be added before any link, meta tags
|
|
||||||
if (!baseTagExists && isString(baseHref)) {
|
|
||||||
rewriter.emitStartTag(tag);
|
|
||||||
rewriter.emitRaw(`<base href="${baseHref}">`);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'base':
|
|
||||||
// Adjust base href if specified
|
|
||||||
if (isString(baseHref)) {
|
|
||||||
updateAttribute(tag, 'href', baseHref);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
rewriter.emitStartTag(tag);
|
|
||||||
})
|
|
||||||
.on('endTag', (tag) => {
|
|
||||||
switch (tag.tagName) {
|
|
||||||
case 'head':
|
|
||||||
for (const linkTag of linkTags) {
|
|
||||||
rewriter.emitRaw(linkTag);
|
|
||||||
}
|
|
||||||
|
|
||||||
linkTags = [];
|
|
||||||
break;
|
|
||||||
case 'body':
|
|
||||||
// Add script tags
|
|
||||||
for (const scriptTag of scriptTags) {
|
|
||||||
rewriter.emitRaw(scriptTag);
|
|
||||||
}
|
|
||||||
|
|
||||||
scriptTags = [];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
rewriter.emitEndTag(tag);
|
|
||||||
});
|
|
||||||
|
|
||||||
const content = await transformedContent;
|
|
||||||
|
|
||||||
if (linkTags.length || scriptTags.length) {
|
|
||||||
// In case no body/head tags are not present (dotnet partial templates)
|
|
||||||
return linkTags.join('') + scriptTags.join('') + content;
|
|
||||||
}
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateSriAttributes(content: string): string {
|
|
||||||
const algo = 'sha384';
|
|
||||||
const hash = createHash(algo).update(content, 'utf8').digest('base64');
|
|
||||||
|
|
||||||
return `integrity="${algo}-${hash}"`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateAttribute(
|
|
||||||
tag: { attrs: { name: string; value: string }[] },
|
|
||||||
name: string,
|
|
||||||
value: string
|
|
||||||
): void {
|
|
||||||
const index = tag.attrs.findIndex((a) => a.name === name);
|
|
||||||
const newValue = { name, value };
|
|
||||||
|
|
||||||
if (index === -1) {
|
|
||||||
tag.attrs.push(newValue);
|
|
||||||
} else {
|
|
||||||
tag.attrs[index] = newValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isString(value: unknown): value is string {
|
|
||||||
return typeof value === 'string';
|
|
||||||
}
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google LLC All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
import { Readable, Writable } from 'stream';
|
|
||||||
|
|
||||||
export async function htmlRewritingStream(content: string): Promise<{
|
|
||||||
rewriter: import('parse5-html-rewriting-stream');
|
|
||||||
transformedContent: Promise<string>;
|
|
||||||
}> {
|
|
||||||
const chunks: Buffer[] = [];
|
|
||||||
const rewriter = new (await import('parse5-html-rewriting-stream'))();
|
|
||||||
|
|
||||||
return {
|
|
||||||
rewriter,
|
|
||||||
transformedContent: new Promise((resolve) => {
|
|
||||||
new Readable({
|
|
||||||
encoding: 'utf8',
|
|
||||||
read(): void {
|
|
||||||
this.push(Buffer.from(content));
|
|
||||||
this.push(null);
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.pipe(rewriter)
|
|
||||||
.pipe(
|
|
||||||
new Writable({
|
|
||||||
write(
|
|
||||||
chunk: string | Buffer,
|
|
||||||
encoding: string | undefined,
|
|
||||||
callback: Function
|
|
||||||
): void {
|
|
||||||
chunks.push(
|
|
||||||
typeof chunk === 'string'
|
|
||||||
? Buffer.from(chunk, encoding as BufferEncoding)
|
|
||||||
: chunk
|
|
||||||
);
|
|
||||||
callback();
|
|
||||||
},
|
|
||||||
final(callback: (error?: Error) => void): void {
|
|
||||||
callback();
|
|
||||||
resolve(Buffer.concat(chunks).toString());
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,147 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google LLC All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import { join } from 'path';
|
|
||||||
import {
|
|
||||||
CrossOriginValue,
|
|
||||||
FileInfo,
|
|
||||||
augmentIndexHtml,
|
|
||||||
} from './augment-index-html';
|
|
||||||
|
|
||||||
function stripBom(data: string) {
|
|
||||||
return data.replace(/^\uFEFF/, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
type IndexHtmlGeneratorPlugin = (
|
|
||||||
html: string,
|
|
||||||
options: IndexHtmlGeneratorProcessOptions
|
|
||||||
) => Promise<string | IndexHtmlTransformResult>;
|
|
||||||
|
|
||||||
export interface IndexHtmlGeneratorProcessOptions {
|
|
||||||
lang?: string | undefined;
|
|
||||||
baseHref?: string | undefined;
|
|
||||||
outputPath: string;
|
|
||||||
files: FileInfo[];
|
|
||||||
noModuleFiles: FileInfo[];
|
|
||||||
moduleFiles: FileInfo[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IndexHtmlGeneratorOptions {
|
|
||||||
indexPath: string;
|
|
||||||
deployUrl?: string;
|
|
||||||
sri?: boolean;
|
|
||||||
entrypoints: string[];
|
|
||||||
postTransform?: IndexHtmlTransform;
|
|
||||||
crossOrigin?: CrossOriginValue;
|
|
||||||
optimization?: any;
|
|
||||||
WOFFSupportNeeded?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type IndexHtmlTransform = (content: string) => Promise<string>;
|
|
||||||
|
|
||||||
export interface IndexHtmlTransformResult {
|
|
||||||
content: string;
|
|
||||||
warnings: string[];
|
|
||||||
errors: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class IndexHtmlGenerator {
|
|
||||||
private readonly plugins: IndexHtmlGeneratorPlugin[];
|
|
||||||
|
|
||||||
constructor(readonly options: IndexHtmlGeneratorOptions) {
|
|
||||||
const extraPlugins: IndexHtmlGeneratorPlugin[] = [];
|
|
||||||
this.plugins = [
|
|
||||||
augmentIndexHtmlPlugin(this),
|
|
||||||
...extraPlugins,
|
|
||||||
postTransformPlugin(this),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
async process(
|
|
||||||
options: IndexHtmlGeneratorProcessOptions
|
|
||||||
): Promise<IndexHtmlTransformResult> {
|
|
||||||
let content = stripBom(await this.readIndex(this.options.indexPath));
|
|
||||||
const warnings: string[] = [];
|
|
||||||
const errors: string[] = [];
|
|
||||||
|
|
||||||
for (const plugin of this.plugins) {
|
|
||||||
const result = await plugin(content, options);
|
|
||||||
if (typeof result === 'string') {
|
|
||||||
content = result;
|
|
||||||
} else {
|
|
||||||
content = result.content;
|
|
||||||
|
|
||||||
if (result.warnings.length) {
|
|
||||||
warnings.push(...result.warnings);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.errors.length) {
|
|
||||||
errors.push(...result.errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
content,
|
|
||||||
warnings,
|
|
||||||
errors,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async readAsset(path: string): Promise<string> {
|
|
||||||
return fs.promises.readFile(path, 'utf-8');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async readIndex(path: string): Promise<string> {
|
|
||||||
return fs.promises.readFile(path, 'utf-8');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function augmentIndexHtmlPlugin(
|
|
||||||
generator: IndexHtmlGenerator
|
|
||||||
): IndexHtmlGeneratorPlugin {
|
|
||||||
const {
|
|
||||||
deployUrl,
|
|
||||||
crossOrigin,
|
|
||||||
sri = false,
|
|
||||||
entrypoints,
|
|
||||||
} = generator.options;
|
|
||||||
|
|
||||||
return async (html, options) => {
|
|
||||||
const {
|
|
||||||
lang,
|
|
||||||
baseHref,
|
|
||||||
outputPath = '',
|
|
||||||
noModuleFiles,
|
|
||||||
files,
|
|
||||||
moduleFiles,
|
|
||||||
} = options;
|
|
||||||
|
|
||||||
return augmentIndexHtml({
|
|
||||||
html,
|
|
||||||
baseHref,
|
|
||||||
deployUrl,
|
|
||||||
crossOrigin,
|
|
||||||
sri,
|
|
||||||
lang,
|
|
||||||
entrypoints,
|
|
||||||
loadOutputFile: (filePath) =>
|
|
||||||
generator.readAsset(join(outputPath, filePath)),
|
|
||||||
noModuleFiles,
|
|
||||||
moduleFiles,
|
|
||||||
files,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function postTransformPlugin({
|
|
||||||
options,
|
|
||||||
}: IndexHtmlGenerator): IndexHtmlGeneratorPlugin {
|
|
||||||
return async (html) =>
|
|
||||||
options.postTransform ? options.postTransform(html) : html;
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function defaultProgress(progress: boolean | undefined): boolean {
|
|
||||||
if (progress === undefined) {
|
|
||||||
return process.stdout.isTTY === true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return progress;
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
import { resolve } from 'path';
|
|
||||||
import { existsSync, unlinkSync } from 'fs';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete an output directory, but error out if it's the root of the project.
|
|
||||||
*/
|
|
||||||
export function deleteOutputDir(root: string, outputPath: string) {
|
|
||||||
const resolvedOutputPath = resolve(root, outputPath);
|
|
||||||
if (resolvedOutputPath === root) {
|
|
||||||
throw new Error('Output path MUST not be project root directory!');
|
|
||||||
}
|
|
||||||
|
|
||||||
const exists = existsSync(resolvedOutputPath);
|
|
||||||
if (exists) {
|
|
||||||
unlinkSync(resolvedOutputPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
export * from './build-browser-features';
|
|
||||||
export * from './default-progress';
|
|
||||||
export * from './delete-output-dir';
|
|
||||||
export * from './normalize-file-replacements';
|
|
||||||
export * from './normalize-asset-patterns';
|
|
||||||
export * from './normalize-source-maps';
|
|
||||||
export * from './normalize-optimization';
|
|
||||||
export * from './normalize-builder-schema';
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
const mangleVariable = process.env['NG_BUILD_MANGLE'];
|
|
||||||
export const manglingDisabled =
|
|
||||||
!!mangleVariable &&
|
|
||||||
(mangleVariable === '0' || mangleVariable.toLowerCase() === 'false');
|
|
||||||
@ -1,73 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
import { normalizePath } from '@nrwl/devkit';
|
|
||||||
import { basename, dirname, relative, join, resolve } from 'path';
|
|
||||||
import { AssetPattern, AssetPatternClass } from '../browser/schema';
|
|
||||||
import { statSync } from 'fs';
|
|
||||||
|
|
||||||
export class MissingAssetSourceRootException extends Error {
|
|
||||||
constructor(path: String) {
|
|
||||||
super(`The ${path} asset path must start with the project source root.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeAssetPatterns(
|
|
||||||
assetPatterns: AssetPattern[],
|
|
||||||
root: string,
|
|
||||||
projectRoot: string,
|
|
||||||
maybeSourceRoot: string | undefined
|
|
||||||
): AssetPatternClass[] {
|
|
||||||
// When sourceRoot is not available, we default to ${projectRoot}/src.
|
|
||||||
const sourceRoot = maybeSourceRoot || join(projectRoot, 'src');
|
|
||||||
const resolvedSourceRoot = resolve(root, sourceRoot);
|
|
||||||
|
|
||||||
if (assetPatterns.length === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return assetPatterns.map((assetPattern) => {
|
|
||||||
// Normalize string asset patterns to objects.
|
|
||||||
if (typeof assetPattern === 'string') {
|
|
||||||
const assetPath = normalizePath(assetPattern);
|
|
||||||
const resolvedAssetPath = resolve(root, assetPath);
|
|
||||||
|
|
||||||
// Check if the string asset is within sourceRoot.
|
|
||||||
if (!resolvedAssetPath.startsWith(resolvedSourceRoot)) {
|
|
||||||
throw new MissingAssetSourceRootException(assetPattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
let glob: string, input: string, output: string;
|
|
||||||
let isDirectory = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
isDirectory = statSync(resolvedAssetPath).isDirectory();
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
if (isDirectory) {
|
|
||||||
// Folders get a recursive star glob.
|
|
||||||
glob = '**/*';
|
|
||||||
// Input directory is their original path.
|
|
||||||
input = assetPath;
|
|
||||||
} else {
|
|
||||||
// Files are their own glob.
|
|
||||||
glob = basename(assetPath);
|
|
||||||
// Input directory is their original dirname.
|
|
||||||
input = dirname(assetPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output directory for both is the relative path from source root to input.
|
|
||||||
output = relative(resolvedSourceRoot, resolve(root, input));
|
|
||||||
|
|
||||||
// Return the asset pattern in object format.
|
|
||||||
return { glob, input, output };
|
|
||||||
} else {
|
|
||||||
// It's already an AssetPatternObject, no need to convert.
|
|
||||||
return assetPattern;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { BuildOptions } from '../cli-files/models/build-options';
|
|
||||||
import {
|
|
||||||
AssetPatternClass,
|
|
||||||
OptimizationClass,
|
|
||||||
Schema as BrowserBuilderSchema,
|
|
||||||
SourceMapClass,
|
|
||||||
} from '../browser/schema';
|
|
||||||
import { normalizeAssetPatterns } from './normalize-asset-patterns';
|
|
||||||
import {
|
|
||||||
NormalizedFileReplacement,
|
|
||||||
normalizeFileReplacements,
|
|
||||||
} from './normalize-file-replacements';
|
|
||||||
import { normalizeOptimization } from './normalize-optimization';
|
|
||||||
import { normalizeSourceMaps } from './normalize-source-maps';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A normalized browser builder schema.
|
|
||||||
*/
|
|
||||||
export type NormalizedBrowserBuilderSchema = BrowserBuilderSchema &
|
|
||||||
BuildOptions & {
|
|
||||||
sourceMap: SourceMapClass;
|
|
||||||
assets: AssetPatternClass[];
|
|
||||||
fileReplacements: NormalizedFileReplacement[];
|
|
||||||
optimization: OptimizationClass;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function normalizeBrowserSchema(
|
|
||||||
root: string,
|
|
||||||
projectRoot: string,
|
|
||||||
sourceRoot: string | undefined,
|
|
||||||
options: BrowserBuilderSchema
|
|
||||||
): NormalizedBrowserBuilderSchema {
|
|
||||||
const normalizedSourceMapOptions = normalizeSourceMaps(
|
|
||||||
options.sourceMap || false
|
|
||||||
);
|
|
||||||
normalizedSourceMapOptions.vendor =
|
|
||||||
normalizedSourceMapOptions.vendor || options.vendorSourceMap;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...options,
|
|
||||||
assets: normalizeAssetPatterns(
|
|
||||||
options.assets || [],
|
|
||||||
root,
|
|
||||||
projectRoot,
|
|
||||||
sourceRoot
|
|
||||||
),
|
|
||||||
fileReplacements: normalizeFileReplacements(
|
|
||||||
options.fileReplacements || [],
|
|
||||||
root
|
|
||||||
),
|
|
||||||
optimization: normalizeOptimization(options.optimization),
|
|
||||||
sourceMap: normalizedSourceMapOptions,
|
|
||||||
|
|
||||||
statsJson: options.statsJson || false,
|
|
||||||
forkTypeChecker: options.forkTypeChecker || false,
|
|
||||||
budgets: options.budgets || [],
|
|
||||||
scripts: options.scripts || [],
|
|
||||||
styles: options.styles || [],
|
|
||||||
stylePreprocessorOptions: {
|
|
||||||
includePaths:
|
|
||||||
(options.stylePreprocessorOptions &&
|
|
||||||
options.stylePreprocessorOptions.includePaths) ||
|
|
||||||
[],
|
|
||||||
},
|
|
||||||
lazyModules: options.lazyModules || [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,77 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { normalizePath } from '@nrwl/devkit';
|
|
||||||
import { join } from 'path';
|
|
||||||
import { existsSync } from 'fs';
|
|
||||||
import { FileReplacement } from '../browser/schema';
|
|
||||||
|
|
||||||
export class MissingFileReplacementException extends Error {
|
|
||||||
constructor(path: String) {
|
|
||||||
super(`The ${path} path in file replacements does not exist.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NormalizedFileReplacement {
|
|
||||||
replace: string;
|
|
||||||
with: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeFileReplacements(
|
|
||||||
fileReplacements: FileReplacement[],
|
|
||||||
root: string
|
|
||||||
): NormalizedFileReplacement[] {
|
|
||||||
if (fileReplacements.length === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalizedReplacement = fileReplacements.map((replacement) =>
|
|
||||||
normalizeFileReplacement(replacement, root)
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const { replace, with: replacementWith } of normalizedReplacement) {
|
|
||||||
if (!existsSync(replacementWith)) {
|
|
||||||
throw new MissingFileReplacementException(replacementWith);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!existsSync(replace)) {
|
|
||||||
throw new MissingFileReplacementException(replace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return normalizedReplacement;
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeFileReplacement(
|
|
||||||
fileReplacement: FileReplacement,
|
|
||||||
root?: string
|
|
||||||
): NormalizedFileReplacement {
|
|
||||||
let replacePath: string;
|
|
||||||
let withPath: string;
|
|
||||||
if (fileReplacement.src && fileReplacement.replaceWith) {
|
|
||||||
replacePath = normalizePath(fileReplacement.src);
|
|
||||||
withPath = normalizePath(fileReplacement.replaceWith);
|
|
||||||
} else if (fileReplacement.replace && fileReplacement.with) {
|
|
||||||
replacePath = normalizePath(fileReplacement.replace);
|
|
||||||
withPath = normalizePath(fileReplacement.with);
|
|
||||||
} else {
|
|
||||||
throw new Error(
|
|
||||||
`Invalid file replacement: ${JSON.stringify(fileReplacement)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: For 7.x should this only happen if not absolute?
|
|
||||||
if (root) {
|
|
||||||
replacePath = join(root, replacePath);
|
|
||||||
}
|
|
||||||
if (root) {
|
|
||||||
withPath = join(root, withPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { replace: replacePath, with: withPath };
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { OptimizationClass, OptimizationUnion } from '../browser/schema';
|
|
||||||
|
|
||||||
export function normalizeOptimization(
|
|
||||||
optimization: OptimizationUnion = false
|
|
||||||
): Required<OptimizationClass> {
|
|
||||||
return {
|
|
||||||
scripts:
|
|
||||||
typeof optimization === 'object' ? !!optimization.scripts : optimization,
|
|
||||||
styles:
|
|
||||||
typeof optimization === 'object' ? !!optimization.styles : optimization,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { SourceMapClass, SourceMapUnion } from '../browser/schema';
|
|
||||||
|
|
||||||
export function normalizeSourceMaps(sourceMap: SourceMapUnion): SourceMapClass {
|
|
||||||
const scripts = typeof sourceMap === 'object' ? sourceMap.scripts : sourceMap;
|
|
||||||
const styles = typeof sourceMap === 'object' ? sourceMap.styles : sourceMap;
|
|
||||||
const hidden = (typeof sourceMap === 'object' && sourceMap.hidden) || false;
|
|
||||||
const vendor = (typeof sourceMap === 'object' && sourceMap.vendor) || false;
|
|
||||||
|
|
||||||
return {
|
|
||||||
vendor,
|
|
||||||
hidden,
|
|
||||||
scripts,
|
|
||||||
styles,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
import { FileReplacement } from './normalize';
|
|
||||||
|
|
||||||
export interface OptimizationOptions {
|
|
||||||
scripts: boolean;
|
|
||||||
styles: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BuildBuilderOptions {
|
|
||||||
main: string;
|
|
||||||
outputPath: string;
|
|
||||||
tsConfig: string;
|
|
||||||
watch?: boolean;
|
|
||||||
sourceMap?: boolean | 'hidden';
|
|
||||||
optimization?: boolean | OptimizationOptions;
|
|
||||||
memoryLimit?: number;
|
|
||||||
maxWorkers?: number;
|
|
||||||
poll?: number;
|
|
||||||
|
|
||||||
fileReplacements?: FileReplacement[];
|
|
||||||
assets?: any[];
|
|
||||||
|
|
||||||
progress?: boolean;
|
|
||||||
statsJson?: boolean;
|
|
||||||
extractLicenses?: boolean;
|
|
||||||
verbose?: boolean;
|
|
||||||
|
|
||||||
outputHashing?: any;
|
|
||||||
webpackConfig?: string;
|
|
||||||
|
|
||||||
root?: string;
|
|
||||||
sourceRoot?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AssetGlobPattern {
|
|
||||||
glob: string;
|
|
||||||
input: string;
|
|
||||||
output: string;
|
|
||||||
ignore?: string[];
|
|
||||||
}
|
|
||||||
@ -1,20 +1,16 @@
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { basename, posix, resolve } from 'path';
|
import { posix, resolve } from 'path';
|
||||||
import { readTsConfig } from '@nrwl/workspace/src/utilities/typescript';
|
import { readTsConfig } from '@nrwl/workspace/src/utilities/typescript';
|
||||||
import { ScriptTarget } from 'typescript';
|
import { ScriptTarget } from 'typescript';
|
||||||
import { getHashDigest, interpolateName } from 'loader-utils';
|
import { getHashDigest, interpolateName } from 'loader-utils';
|
||||||
import { Configuration } from 'webpack';
|
import { Configuration } from 'webpack';
|
||||||
|
|
||||||
// TODO @FrozenPandaz we should remove the following imports
|
import { WebBuildExecutorOptions } from '../executors/build/build.impl';
|
||||||
import { getBrowserConfig } from './third-party/cli-files/models/webpack-configs/browser';
|
|
||||||
import { getCommonConfig } from './third-party/cli-files/models/webpack-configs/common';
|
|
||||||
import { getStylesConfig } from './third-party/cli-files/models/webpack-configs/styles';
|
|
||||||
import { IndexHtmlWebpackPlugin } from './third-party/cli-files/plugins/index-html-webpack-plugin';
|
|
||||||
import { generateEntryPoints } from './third-party/cli-files/utilities/package-chunk-sort';
|
|
||||||
|
|
||||||
import { WebBuildBuilderOptions } from '../executors/build/build.impl';
|
|
||||||
import { convertBuildOptions } from './normalize';
|
import { convertBuildOptions } from './normalize';
|
||||||
import { getBaseWebpackPartial } from './config';
|
import { getBaseWebpackPartial } from './config';
|
||||||
|
import { getBrowserConfig } from './webpack/partials/browser';
|
||||||
|
import { getCommonConfig } from './webpack/partials/common';
|
||||||
|
import { getStylesConfig } from './webpack/partials/styles';
|
||||||
import MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
import MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
import webpackMerge = require('webpack-merge');
|
import webpackMerge = require('webpack-merge');
|
||||||
import postcssImports = require('postcss-import');
|
import postcssImports = require('postcss-import');
|
||||||
@ -31,7 +27,7 @@ export function getWebConfig(
|
|||||||
workspaceRoot,
|
workspaceRoot,
|
||||||
projectRoot,
|
projectRoot,
|
||||||
sourceRoot,
|
sourceRoot,
|
||||||
options: WebBuildBuilderOptions,
|
options: WebBuildExecutorOptions,
|
||||||
esm?: boolean,
|
esm?: boolean,
|
||||||
isScriptOptimizeOn?: boolean,
|
isScriptOptimizeOn?: boolean,
|
||||||
configuration?: string
|
configuration?: string
|
||||||
@ -69,53 +65,18 @@ export function getWebConfig(
|
|||||||
esm,
|
esm,
|
||||||
isScriptOptimizeOn
|
isScriptOptimizeOn
|
||||||
),
|
),
|
||||||
getStylesPartial(
|
getStylesPartial(wco.root, wco.projectRoot, wco.buildOptions, true),
|
||||||
wco.root,
|
|
||||||
wco.projectRoot,
|
|
||||||
wco.buildOptions,
|
|
||||||
options.extractCss
|
|
||||||
),
|
|
||||||
getCommonPartial(wco),
|
getCommonPartial(wco),
|
||||||
getBrowserPartial(wco, options, isScriptOptimizeOn),
|
getBrowserPartial(wco, options),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBrowserPartial(
|
function getBrowserPartial(wco: any, options: WebBuildExecutorOptions) {
|
||||||
wco: any,
|
return getBrowserConfig(wco);
|
||||||
options: WebBuildBuilderOptions,
|
|
||||||
isScriptOptimizeOn: boolean
|
|
||||||
) {
|
|
||||||
const config = getBrowserConfig(wco);
|
|
||||||
|
|
||||||
if (!isScriptOptimizeOn) {
|
|
||||||
const {
|
|
||||||
deployUrl,
|
|
||||||
subresourceIntegrity,
|
|
||||||
scripts = [],
|
|
||||||
styles = [],
|
|
||||||
index,
|
|
||||||
baseHref,
|
|
||||||
} = options;
|
|
||||||
|
|
||||||
config.plugins.push(
|
|
||||||
new IndexHtmlWebpackPlugin({
|
|
||||||
indexPath: resolve(wco.root, index),
|
|
||||||
outputPath: basename(index),
|
|
||||||
baseHref,
|
|
||||||
entrypoints: generateEntryPoints({ scripts, styles }),
|
|
||||||
deployUrl,
|
|
||||||
sri: subresourceIntegrity,
|
|
||||||
moduleEntrypoints: [],
|
|
||||||
noModuleEntrypoints: ['polyfills-es5'],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _getBaseWebpackPartial(
|
function _getBaseWebpackPartial(
|
||||||
options: WebBuildBuilderOptions,
|
options: WebBuildExecutorOptions,
|
||||||
esm: boolean,
|
esm: boolean,
|
||||||
isScriptOptimizeOn: boolean,
|
isScriptOptimizeOn: boolean,
|
||||||
emitDecoratorMetadata: boolean,
|
emitDecoratorMetadata: boolean,
|
||||||
@ -300,9 +261,7 @@ export function getPolyfillsPartial(
|
|||||||
// Safari 10.1 supports <script type="module"> but not <script nomodule>.
|
// Safari 10.1 supports <script type="module"> but not <script nomodule>.
|
||||||
// Need to patch it up so the browser doesn't load both sets.
|
// Need to patch it up so the browser doesn't load both sets.
|
||||||
config.entry.polyfills = [
|
config.entry.polyfills = [
|
||||||
require.resolve(
|
require.resolve('@nrwl/web/src/utils/webpack/safari-nomodule.js'),
|
||||||
'@nrwl/web/src/utils/third-party/cli-files/models/safari-nomodule.js'
|
|
||||||
),
|
|
||||||
...(polyfills ? [polyfills] : []),
|
...(polyfills ? [polyfills] : []),
|
||||||
];
|
];
|
||||||
} else if (es2015Polyfills && !esm && isScriptOptimizeOn) {
|
} else if (es2015Polyfills && !esm && isScriptOptimizeOn) {
|
||||||
|
|||||||
@ -1,11 +1,3 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { fs, vol } from 'memfs';
|
import { fs, vol } from 'memfs';
|
||||||
jest.mock('fs', () => fs);
|
jest.mock('fs', () => fs);
|
||||||
import { ScriptTarget } from 'typescript';
|
import { ScriptTarget } from 'typescript';
|
||||||
@ -1,21 +1,7 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as browserslist from 'browserslist';
|
import * as browserslist from 'browserslist';
|
||||||
import { feature, features } from 'caniuse-lite';
|
import { feature, features } from 'caniuse-lite';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
const fullDifferentialEnv = process.env['NG_BUILD_DIFFERENTIAL_FULL'];
|
|
||||||
export const fullDifferential =
|
|
||||||
fullDifferentialEnv !== undefined &&
|
|
||||||
fullDifferentialEnv !== '0' &&
|
|
||||||
fullDifferentialEnv.toLowerCase() !== 'false';
|
|
||||||
|
|
||||||
export class BuildBrowserFeatures {
|
export class BuildBrowserFeatures {
|
||||||
private readonly _supportedBrowsers: string[];
|
private readonly _supportedBrowsers: string[];
|
||||||
private readonly _es6TargetOrLater: boolean;
|
private readonly _es6TargetOrLater: boolean;
|
||||||
@ -1,12 +1,5 @@
|
|||||||
/**
|
import { normalizeExtraEntryPoints } from '../normalize';
|
||||||
* @license
|
import { ExtraEntryPoint } from '../shared-models';
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
import { ExtraEntryPoint } from '../../browser/schema';
|
|
||||||
import { normalizeExtraEntryPoints } from '../models/webpack-configs/utils';
|
|
||||||
|
|
||||||
export function generateEntryPoints(appConfig: {
|
export function generateEntryPoints(appConfig: {
|
||||||
styles: ExtraEntryPoint[];
|
styles: ExtraEntryPoint[];
|
||||||
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import { LicenseWebpackPlugin } from 'license-webpack-plugin';
|
import { LicenseWebpackPlugin } from 'license-webpack-plugin';
|
||||||
import { WebpackConfigOptions } from '../build-options';
|
import { WebpackConfigOptions } from '../../shared-models';
|
||||||
|
|
||||||
const SubresourceIntegrityPlugin = require('webpack-subresource-integrity');
|
const SubresourceIntegrityPlugin = require('webpack-subresource-integrity');
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ export function getBrowserConfig(wco: WebpackConfigOptions) {
|
|||||||
: false,
|
: false,
|
||||||
},
|
},
|
||||||
optimization: {
|
optimization: {
|
||||||
runtimeChunk: !!buildOptions.runtimeChunk ? 'single' : false,
|
runtimeChunk: 'single',
|
||||||
splitChunks: {
|
splitChunks: {
|
||||||
maxAsyncRequests: Infinity,
|
maxAsyncRequests: Infinity,
|
||||||
cacheGroups: {
|
cacheGroups: {
|
||||||
@ -1,56 +1,33 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { ScriptTarget } from 'typescript';
|
import { ScriptTarget } from 'typescript';
|
||||||
import * as webpack from 'webpack';
|
import * as webpack from 'webpack';
|
||||||
import { Compiler, Configuration } from 'webpack';
|
import { Compiler, Configuration } from 'webpack';
|
||||||
|
|
||||||
import { ExtraEntryPoint } from '../../../browser/schema';
|
import { ScriptsWebpackPlugin } from '../plugins/scripts-webpack-plugin';
|
||||||
import { BuildBrowserFeatures, fullDifferential } from '../../../utils';
|
import { ExtraEntryPoint, WebpackConfigOptions } from '../../shared-models';
|
||||||
import { manglingDisabled } from '../../../utils/mangle-options';
|
import { BuildBrowserFeatures } from '../build-browser-features';
|
||||||
import { ScriptsWebpackPlugin } from '../../plugins/scripts-webpack-plugin';
|
import { getOutputHashFormat } from '../../hash-format';
|
||||||
import { findAllNodeModules, findUp } from '../../utilities/find-up';
|
import { normalizeExtraEntryPoints } from '../../normalize';
|
||||||
import { WebpackConfigOptions } from '../build-options';
|
|
||||||
import {
|
|
||||||
getEsVersionForFileName,
|
|
||||||
getOutputHashFormat,
|
|
||||||
normalizeExtraEntryPoints,
|
|
||||||
} from './utils';
|
|
||||||
import CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
import CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||||
|
import { findAllNodeModules, findUp } from '../../fs';
|
||||||
export const GLOBAL_DEFS_FOR_TERSER = {
|
|
||||||
ngDevMode: false,
|
|
||||||
ngI18nClosureMode: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const GLOBAL_DEFS_FOR_TERSER_WITH_AOT = {
|
|
||||||
...GLOBAL_DEFS_FOR_TERSER,
|
|
||||||
ngJitMode: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
|
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
|
||||||
const TerserPlugin = require('terser-webpack-plugin');
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
|
|
||||||
// tslint:disable-next-line:no-any
|
|
||||||
const g: any = typeof global !== 'undefined' ? global : {};
|
|
||||||
|
|
||||||
// tslint:disable-next-line:no-big-function
|
// tslint:disable-next-line:no-big-function
|
||||||
export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
|
export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
|
||||||
const { ContextReplacementPlugin, debug } = webpack;
|
const { ContextReplacementPlugin } = webpack;
|
||||||
|
|
||||||
const { root, projectRoot, sourceRoot, buildOptions, tsConfig } = wco;
|
const { root, projectRoot, sourceRoot, buildOptions, tsConfig } = wco;
|
||||||
const { styles: stylesOptimization, scripts: scriptsOptimization } =
|
|
||||||
buildOptions.optimization;
|
let stylesOptimization: boolean;
|
||||||
const {
|
let scriptsOptimization: boolean;
|
||||||
styles: stylesSourceMap,
|
if (typeof buildOptions.optimization === 'object') {
|
||||||
scripts: scriptsSourceMap,
|
scriptsOptimization = buildOptions.optimization.scripts;
|
||||||
vendor: vendorSourceMap,
|
stylesOptimization = buildOptions.optimization.styles;
|
||||||
} = buildOptions.sourceMap;
|
} else {
|
||||||
|
scriptsOptimization = stylesOptimization = !!buildOptions.optimization;
|
||||||
|
}
|
||||||
|
|
||||||
const nodeModules = findUp('node_modules', projectRoot);
|
const nodeModules = findUp('node_modules', projectRoot);
|
||||||
if (!nodeModules) {
|
if (!nodeModules) {
|
||||||
@ -61,108 +38,57 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
|
|||||||
const extraPlugins: any[] = [];
|
const extraPlugins: any[] = [];
|
||||||
const entryPoints: { [key: string]: string[] } = {};
|
const entryPoints: { [key: string]: string[] } = {};
|
||||||
|
|
||||||
const targetInFileName = getEsVersionForFileName(
|
|
||||||
fullDifferential
|
|
||||||
? buildOptions.scriptTargetOverride
|
|
||||||
: tsConfig.options.target,
|
|
||||||
buildOptions.esVersionInFileName
|
|
||||||
);
|
|
||||||
|
|
||||||
if (buildOptions.main) {
|
if (buildOptions.main) {
|
||||||
entryPoints['main'] = [path.resolve(root, buildOptions.main)];
|
entryPoints['main'] = [path.resolve(root, buildOptions.main)];
|
||||||
}
|
}
|
||||||
|
|
||||||
let differentialLoadingNeeded = false;
|
const buildBrowserFeatures = new BuildBrowserFeatures(
|
||||||
if (wco.buildOptions.platform !== 'server') {
|
projectRoot,
|
||||||
const buildBrowserFeatures = new BuildBrowserFeatures(
|
tsConfig.options.target || ScriptTarget.ES5
|
||||||
projectRoot,
|
);
|
||||||
tsConfig.options.target || ScriptTarget.ES5
|
|
||||||
);
|
|
||||||
|
|
||||||
differentialLoadingNeeded =
|
const differentialLoadingNeeded =
|
||||||
buildBrowserFeatures.isDifferentialLoadingNeeded();
|
buildBrowserFeatures.isDifferentialLoadingNeeded();
|
||||||
|
|
||||||
if (
|
if (tsConfig.options.target === ScriptTarget.ES5) {
|
||||||
(buildOptions.scriptTargetOverride || tsConfig.options.target) ===
|
if (buildBrowserFeatures.isEs5SupportNeeded()) {
|
||||||
ScriptTarget.ES5
|
// The nomodule polyfill needs to be inject prior to any script and be
|
||||||
) {
|
// outside of webpack compilation because otherwise webpack will cause the
|
||||||
if (
|
// script to be wrapped in window["webpackJsonp"] which causes this to fail.
|
||||||
buildOptions.es5BrowserSupport ||
|
if (buildBrowserFeatures.isNoModulePolyfillNeeded()) {
|
||||||
(buildOptions.es5BrowserSupport === undefined &&
|
const noModuleScript: ExtraEntryPoint = {
|
||||||
buildBrowserFeatures.isEs5SupportNeeded())
|
bundleName: 'polyfills-nomodule-es5',
|
||||||
) {
|
input: path.join(__dirname, '..', 'safari-nomodule.js'),
|
||||||
// The nomodule polyfill needs to be inject prior to any script and be
|
};
|
||||||
// outside of webpack compilation because otherwise webpack will cause the
|
buildOptions.scripts = buildOptions.scripts
|
||||||
// script to be wrapped in window["webpackJsonp"] which causes this to fail.
|
? [...buildOptions.scripts, noModuleScript]
|
||||||
if (buildBrowserFeatures.isNoModulePolyfillNeeded()) {
|
: [noModuleScript];
|
||||||
const noModuleScript: ExtraEntryPoint = {
|
|
||||||
bundleName: 'polyfills-nomodule-es5',
|
|
||||||
input: path.join(__dirname, '..', 'safari-nomodule.js'),
|
|
||||||
};
|
|
||||||
buildOptions.scripts = buildOptions.scripts
|
|
||||||
? [...buildOptions.scripts, noModuleScript]
|
|
||||||
: [noModuleScript];
|
|
||||||
}
|
|
||||||
|
|
||||||
// For full build differential loading we don't need to generate a seperate polyfill file
|
|
||||||
// because they will be loaded exclusivly based on module and nomodule
|
|
||||||
const polyfillsChunkName =
|
|
||||||
fullDifferential && differentialLoadingNeeded
|
|
||||||
? 'polyfills'
|
|
||||||
: 'polyfills-es5';
|
|
||||||
|
|
||||||
entryPoints[polyfillsChunkName] = [
|
|
||||||
path.join(__dirname, '..', 'es5-polyfills.js'),
|
|
||||||
];
|
|
||||||
if (!fullDifferential && differentialLoadingNeeded) {
|
|
||||||
// Add zone.js legacy support to the es5 polyfills
|
|
||||||
// This is a noop execution-wise if zone-evergreen is not used.
|
|
||||||
entryPoints[polyfillsChunkName].push('zone.js/dist/zone-legacy');
|
|
||||||
}
|
|
||||||
if (!buildOptions.aot) {
|
|
||||||
// If not performing a full differential build the JIT polyfills need to be added to ES5
|
|
||||||
if (!fullDifferential && differentialLoadingNeeded) {
|
|
||||||
entryPoints[polyfillsChunkName].push(
|
|
||||||
path.join(__dirname, '..', 'jit-polyfills.js')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
entryPoints[polyfillsChunkName].push(
|
|
||||||
path.join(__dirname, '..', 'es5-jit-polyfills.js')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// If not performing a full differential build the polyfills need to be added to ES5 bundle
|
|
||||||
if (!fullDifferential && buildOptions.polyfills) {
|
|
||||||
entryPoints[polyfillsChunkName].push(
|
|
||||||
path.resolve(root, buildOptions.polyfills)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (buildOptions.polyfills) {
|
// For full build differential loading we don't need to generate a seperate polyfill file
|
||||||
entryPoints['polyfills'] = [
|
// because they will be loaded exclusivly based on module and nomodule
|
||||||
...(entryPoints['polyfills'] || []),
|
const polyfillsChunkName = differentialLoadingNeeded
|
||||||
path.resolve(root, buildOptions.polyfills),
|
? 'polyfills'
|
||||||
];
|
: 'polyfills-es5';
|
||||||
}
|
|
||||||
|
|
||||||
if (!buildOptions.aot) {
|
entryPoints[polyfillsChunkName] = [
|
||||||
entryPoints['polyfills'] = [
|
path.join(__dirname, '..', 'es5-polyfills.js'),
|
||||||
...(entryPoints['polyfills'] || []),
|
|
||||||
path.join(__dirname, '..', 'jit-polyfills.js'),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// If not performing a full differential build the polyfills need to be added to ES5 bundle
|
||||||
|
if (buildOptions.polyfills) {
|
||||||
|
entryPoints[polyfillsChunkName].push(
|
||||||
|
path.resolve(root, buildOptions.polyfills)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buildOptions.profile || process.env['NG_BUILD_PROFILING']) {
|
if (buildOptions.polyfills) {
|
||||||
extraPlugins.push(
|
entryPoints['polyfills'] = [
|
||||||
new debug.ProfilingPlugin({
|
...(entryPoints['polyfills'] || []),
|
||||||
outputPath: path.resolve(
|
path.resolve(root, buildOptions.polyfills),
|
||||||
root,
|
];
|
||||||
`chrome-profiler-events${targetInFileName}.json`
|
|
||||||
),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine hashing format
|
// determine hashing format
|
||||||
@ -212,7 +138,7 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
|
|||||||
extraPlugins.push(
|
extraPlugins.push(
|
||||||
new ScriptsWebpackPlugin({
|
new ScriptsWebpackPlugin({
|
||||||
name: bundleName,
|
name: bundleName,
|
||||||
sourceMap: scriptsSourceMap,
|
sourceMap: !!buildOptions.sourceMap,
|
||||||
filename: `${path.basename(bundleName)}${hash}.js`,
|
filename: `${path.basename(bundleName)}${hash}.js`,
|
||||||
scripts: script.paths,
|
scripts: script.paths,
|
||||||
basePath: sourceRoot,
|
basePath: sourceRoot,
|
||||||
@ -234,8 +160,9 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
|
|||||||
const data = JSON.stringify(
|
const data = JSON.stringify(
|
||||||
compilation.getStats().toJson('verbose')
|
compilation.getStats().toJson('verbose')
|
||||||
);
|
);
|
||||||
compilation.assets[`stats${targetInFileName}.json`] =
|
compilation.assets[`stats.json`] = new webpack.sources.RawSource(
|
||||||
new webpack.sources.RawSource(data);
|
data
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
@ -243,7 +170,7 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let sourceMapUseRule;
|
let sourceMapUseRule;
|
||||||
if ((scriptsSourceMap || stylesSourceMap) && vendorSourceMap) {
|
if (!!buildOptions.sourceMap) {
|
||||||
sourceMapUseRule = {
|
sourceMapUseRule = {
|
||||||
use: [
|
use: [
|
||||||
{
|
{
|
||||||
@ -282,25 +209,6 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (scriptsOptimization) {
|
if (scriptsOptimization) {
|
||||||
let angularGlobalDefinitions = {
|
|
||||||
ngDevMode: false,
|
|
||||||
ngI18nClosureMode: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (GLOBAL_DEFS_FOR_TERSER) {
|
|
||||||
angularGlobalDefinitions = GLOBAL_DEFS_FOR_TERSER;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buildOptions.aot) {
|
|
||||||
// Also try to load AOT-only global definitions.
|
|
||||||
if (GLOBAL_DEFS_FOR_TERSER_WITH_AOT) {
|
|
||||||
angularGlobalDefinitions = {
|
|
||||||
...angularGlobalDefinitions,
|
|
||||||
...GLOBAL_DEFS_FOR_TERSER_WITH_AOT,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Investigate why this fails for some packages: wco.supportES2015 ? 6 : 5;
|
// TODO: Investigate why this fails for some packages: wco.supportES2015 ? 6 : 5;
|
||||||
const terserEcma = 5;
|
const terserEcma = 5;
|
||||||
|
|
||||||
@ -314,28 +222,15 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
|
|||||||
},
|
},
|
||||||
// On server, we don't want to compress anything. We still set the ngDevMode = false for it
|
// On server, we don't want to compress anything. We still set the ngDevMode = false for it
|
||||||
// to remove dev code, and ngI18nClosureMode to remove Closure compiler i18n code
|
// to remove dev code, and ngI18nClosureMode to remove Closure compiler i18n code
|
||||||
compress:
|
compress: {
|
||||||
buildOptions.platform == 'server'
|
ecma: terserEcma,
|
||||||
? {
|
// TODO(jack): Investigate options to enable further optimizations
|
||||||
ecma: terserEcma,
|
// pure_getters: true,
|
||||||
global_defs: angularGlobalDefinitions,
|
// PURE comments work best with 3 passes.
|
||||||
keep_fnames: true,
|
// See https://github.com/webpack/webpack/issues/2899#issuecomment-317425926.
|
||||||
}
|
// passes: 3,
|
||||||
: {
|
},
|
||||||
ecma: terserEcma,
|
mangle: true,
|
||||||
pure_getters: buildOptions.buildOptimizer,
|
|
||||||
// PURE comments work best with 3 passes.
|
|
||||||
// See https://github.com/webpack/webpack/issues/2899#issuecomment-317425926.
|
|
||||||
passes: buildOptions.buildOptimizer ? 3 : 1,
|
|
||||||
global_defs: angularGlobalDefinitions,
|
|
||||||
},
|
|
||||||
// We also want to avoid mangling on server.
|
|
||||||
// Name mangling is handled within the browser builder
|
|
||||||
mangle:
|
|
||||||
!manglingDisabled &&
|
|
||||||
buildOptions.platform !== 'server' &&
|
|
||||||
(!differentialLoadingNeeded ||
|
|
||||||
(differentialLoadingNeeded && fullDifferential)),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const es5TerserOptions = {
|
const es5TerserOptions = {
|
||||||
@ -348,7 +243,6 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
|
|||||||
...terserOptions.output,
|
...terserOptions.output,
|
||||||
ecma: 5,
|
ecma: 5,
|
||||||
},
|
},
|
||||||
mangle: !manglingDisabled && buildOptions.platform !== 'server',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extraMinimizers.push(
|
extraMinimizers.push(
|
||||||
@ -366,7 +260,7 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
|
|||||||
profile: buildOptions.statsJson,
|
profile: buildOptions.statsJson,
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.ts', '.tsx', '.mjs', '.js'],
|
extensions: ['.ts', '.tsx', '.mjs', '.js'],
|
||||||
symlinks: !buildOptions.preserveSymlinks,
|
symlinks: true,
|
||||||
modules: [wco.tsConfig.options.baseUrl || projectRoot, 'node_modules'],
|
modules: [wco.tsConfig.options.baseUrl || projectRoot, 'node_modules'],
|
||||||
alias,
|
alias,
|
||||||
},
|
},
|
||||||
@ -380,9 +274,6 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
|
|||||||
publicPath: buildOptions.deployUrl,
|
publicPath: buildOptions.deployUrl,
|
||||||
},
|
},
|
||||||
watch: buildOptions.watch,
|
watch: buildOptions.watch,
|
||||||
watchOptions: {
|
|
||||||
poll: buildOptions.poll,
|
|
||||||
},
|
|
||||||
performance: {
|
performance: {
|
||||||
hints: false,
|
hints: false,
|
||||||
},
|
},
|
||||||
@ -1,51 +1,31 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { RuleSetRule } from 'webpack';
|
import { RuleSetRule } from 'webpack';
|
||||||
|
|
||||||
import {
|
import { WebBuildExecutorOptions } from '../../../executors/build/build.impl';
|
||||||
PostcssCliResources,
|
import { RemoveEmptyScriptsPlugin } from '../plugins/remove-empty-scripts-plugin';
|
||||||
RawCssLoader,
|
import { getOutputHashFormat } from '../../hash-format';
|
||||||
RemoveHashPlugin,
|
import { normalizeExtraEntryPoints } from '../../normalize';
|
||||||
} from '../../plugins/webpack';
|
import { PostcssCliResources } from '../plugins/postcss-cli-resources';
|
||||||
import { BuildOptions } from '../build-options';
|
import { RemoveHashPlugin } from '../plugins/remove-hash-plugin';
|
||||||
import { getOutputHashFormat, normalizeExtraEntryPoints } from './utils';
|
|
||||||
import { RemoveEmptyScriptsPlugin } from '../../plugins/remove-empty-scripts-plugin';
|
|
||||||
import MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
import MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
|
||||||
const autoprefixer = require('autoprefixer');
|
const autoprefixer = require('autoprefixer');
|
||||||
const postcssImports = require('postcss-import');
|
const postcssImports = require('postcss-import');
|
||||||
|
|
||||||
/**
|
const RawCssLoader = require.resolve(
|
||||||
* Enumerate loaders and their dependencies from this file to let the dependency validator
|
path.join(__dirname, '../plugins/raw-css-loader.js')
|
||||||
* know they are used.
|
);
|
||||||
*
|
|
||||||
* require('style-loader')
|
|
||||||
* require('postcss-loader')
|
|
||||||
* require('stylus')
|
|
||||||
* require('stylus-loader')
|
|
||||||
* require('less')
|
|
||||||
* require('less-loader')
|
|
||||||
* require('node-sass')
|
|
||||||
* require('sass-loader')
|
|
||||||
*/
|
|
||||||
// tslint:disable-next-line:no-big-function
|
|
||||||
export function getStylesConfig(
|
export function getStylesConfig(
|
||||||
root: string,
|
root: string,
|
||||||
buildOptions: BuildOptions,
|
buildOptions: WebBuildExecutorOptions,
|
||||||
includePaths: string[]
|
includePaths: string[]
|
||||||
) {
|
) {
|
||||||
const entryPoints: { [key: string]: string[] } = {};
|
const entryPoints: { [key: string]: string[] } = {};
|
||||||
const globalStylePaths: string[] = [];
|
const globalStylePaths: string[] = [];
|
||||||
const extraPlugins = [];
|
const extraPlugins = [];
|
||||||
|
|
||||||
const cssSourceMap = buildOptions.sourceMap.styles;
|
const cssSourceMap = !!buildOptions.sourceMap;
|
||||||
|
|
||||||
// Determine hashing format.
|
// Determine hashing format.
|
||||||
const hashFormat = getOutputHashFormat(buildOptions.outputHashing as string);
|
const hashFormat = getOutputHashFormat(buildOptions.outputHashing as string);
|
||||||
@ -78,7 +58,6 @@ export function getStylesConfig(
|
|||||||
PostcssCliResources({
|
PostcssCliResources({
|
||||||
baseHref: buildOptions.baseHref,
|
baseHref: buildOptions.baseHref,
|
||||||
deployUrl: buildOptions.deployUrl,
|
deployUrl: buildOptions.deployUrl,
|
||||||
resourcesOutputPath: buildOptions.resourcesOutputPath,
|
|
||||||
loader,
|
loader,
|
||||||
filename: `[name]${hashFormat.file}.[ext]`,
|
filename: `[name]${hashFormat.file}.[ext]`,
|
||||||
}),
|
}),
|
||||||
@ -178,24 +157,24 @@ export function getStylesConfig(
|
|||||||
];
|
];
|
||||||
|
|
||||||
// load component css as raw strings
|
// load component css as raw strings
|
||||||
const componentsSourceMap = !!(
|
const componentsSourceMap = !!(cssSourceMap &&
|
||||||
cssSourceMap &&
|
// Never use component css sourcemap when style optimizations are on.
|
||||||
// Never use component css sourcemap when style optimizations are on.
|
// It will just increase bundle size without offering good debug experience.
|
||||||
// It will just increase bundle size without offering good debug experience.
|
typeof buildOptions.optimization === 'undefined'
|
||||||
!buildOptions.optimization.styles &&
|
? true
|
||||||
// Inline all sourcemap types except hidden ones, which are the same as no sourcemaps
|
: typeof buildOptions.optimization === 'boolean'
|
||||||
// for component css.
|
? !buildOptions.optimization
|
||||||
!buildOptions.sourceMap.hidden
|
: buildOptions.optimization.styles &&
|
||||||
);
|
// Inline all sourcemap types except hidden ones, which are the same as no sourcemaps
|
||||||
|
// for component css.
|
||||||
|
buildOptions.sourceMap !== 'hidden');
|
||||||
|
|
||||||
const rules: RuleSetRule[] = baseRules.map(({ test, use }) => ({
|
const rules: RuleSetRule[] = baseRules.map(({ test, use }) => ({
|
||||||
exclude: globalStylePaths,
|
exclude: globalStylePaths,
|
||||||
test,
|
test,
|
||||||
use: [
|
use: [
|
||||||
{ loader: require.resolve('raw-loader') },
|
{ loader: require.resolve('raw-loader') },
|
||||||
// Including RawCssLoader here because per v4.x release notes for postcss-loader under breaking changes:
|
{ loader: RawCssLoader },
|
||||||
// "loader output only CSS, so you need to use css-loader/file-loader/raw-loader to inject code inside bundle"
|
|
||||||
RawCssLoader,
|
|
||||||
{
|
{
|
||||||
loader: require.resolve('postcss-loader'),
|
loader: require.resolve('postcss-loader'),
|
||||||
options: {
|
options: {
|
||||||
@ -203,13 +182,14 @@ export function getStylesConfig(
|
|||||||
postcssOptions: postcssOptionsCreator(componentsSourceMap),
|
postcssOptions: postcssOptionsCreator(componentsSourceMap),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
...(use as any[]),
|
...(Array.isArray(use) ? use : []),
|
||||||
],
|
],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// load global css as css files
|
// load global css as css files
|
||||||
if (globalStylePaths.length > 0) {
|
if (globalStylePaths.length > 0) {
|
||||||
const globalSourceMap = !!cssSourceMap && !buildOptions.sourceMap.hidden;
|
const globalSourceMap =
|
||||||
|
!!cssSourceMap && buildOptions.sourceMap !== 'hidden';
|
||||||
|
|
||||||
rules.push(
|
rules.push(
|
||||||
...baseRules.map(({ test, use }) => {
|
...baseRules.map(({ test, use }) => {
|
||||||
@ -217,13 +197,11 @@ export function getStylesConfig(
|
|||||||
include: globalStylePaths,
|
include: globalStylePaths,
|
||||||
test,
|
test,
|
||||||
use: [
|
use: [
|
||||||
buildOptions.extractCss
|
{
|
||||||
? {
|
loader: MiniCssExtractPlugin.loader,
|
||||||
loader: MiniCssExtractPlugin.loader,
|
options: { esModule: true },
|
||||||
options: { esModule: true },
|
},
|
||||||
}
|
{ loader: RawCssLoader },
|
||||||
: require.resolve('style-loader'),
|
|
||||||
RawCssLoader,
|
|
||||||
{
|
{
|
||||||
loader: require.resolve('postcss-loader'),
|
loader: require.resolve('postcss-loader'),
|
||||||
options: {
|
options: {
|
||||||
@ -238,16 +216,14 @@ export function getStylesConfig(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buildOptions.extractCss) {
|
extraPlugins.push(
|
||||||
extraPlugins.push(
|
// extract global css from js files into own css file
|
||||||
// extract global css from js files into own css file
|
new MiniCssExtractPlugin({
|
||||||
new MiniCssExtractPlugin({
|
filename: `[name]${hashFormat.extract}.css`,
|
||||||
filename: `[name]${hashFormat.extract}.css`,
|
}),
|
||||||
}),
|
// suppress empty .js files in css only entry points
|
||||||
// suppress empty .js files in css only entry points
|
new RemoveEmptyScriptsPlugin()
|
||||||
new RemoveEmptyScriptsPlugin()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entry: entryPoints,
|
entry: entryPoints,
|
||||||
@ -44,7 +44,8 @@ async function resolve(
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports.postcss = true;
|
module.exports.postcss = true;
|
||||||
export default (options: PostcssCliResourcesOptions) => {
|
|
||||||
|
export function PostcssCliResources(options: PostcssCliResourcesOptions) {
|
||||||
const {
|
const {
|
||||||
deployUrl = '',
|
deployUrl = '',
|
||||||
baseHref = '',
|
baseHref = '',
|
||||||
@ -198,4 +199,4 @@ export default (options: PostcssCliResourcesOptions) => {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
5
packages/web/src/utils/webpack/plugins/raw-css-loader.ts
Normal file
5
packages/web/src/utils/webpack/plugins/raw-css-loader.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export default function RawCssLoader(content: string, map: object) {
|
||||||
|
const stringifiedContent = JSON.stringify(content);
|
||||||
|
const stringifiedMap = map ? JSON.stringify(map) : `''`;
|
||||||
|
return `module.exports = [[module.id, ${stringifiedContent}, '', ${stringifiedMap}]]`;
|
||||||
|
}
|
||||||
@ -1,11 +1,4 @@
|
|||||||
/**
|
import { HashFormat } from '../../hash-format';
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
import { HashFormat } from '../models/webpack-configs/utils';
|
|
||||||
|
|
||||||
type Compiler = any;
|
type Compiler = any;
|
||||||
|
|
||||||
@ -1,20 +1,3 @@
|
|||||||
// tslint:disable
|
|
||||||
// TODO: cleanup this file, it's copied as is from Angular CLI.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
import { interpolateName } from 'loader-utils';
|
import { interpolateName } from 'loader-utils';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { Compiler } from 'webpack';
|
import { Compiler } from 'webpack';
|
||||||
@ -1,14 +1,16 @@
|
|||||||
/**
|
import { dirname, join } from 'path';
|
||||||
* @license
|
import { readFileSync, writeFileSync } from 'fs';
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
import { EmittedFile } from '../run-webpack';
|
||||||
*
|
import { ExtraEntryPoint } from '../shared-models';
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
import { interpolateEnvironmentVariablesToIndex } from '../interpolate-env-variables-to-index';
|
||||||
* found in the LICENSE file at https://angular.io/license
|
import { generateEntryPoints } from './package-chunk-sort';
|
||||||
*/
|
|
||||||
|
|
||||||
import { createHash } from 'crypto';
|
import { createHash } from 'crypto';
|
||||||
import { RawSource, ReplaceSource } from 'webpack-sources';
|
import { RawSource, ReplaceSource } from 'webpack-sources';
|
||||||
|
|
||||||
|
function stripBom(data: string) {
|
||||||
|
return data.replace(/^\uFEFF/, '');
|
||||||
|
}
|
||||||
|
|
||||||
const parse5 = require('parse5');
|
const parse5 = require('parse5');
|
||||||
|
|
||||||
export type LoadOutputFileFunctionType = (file: string) => string;
|
export type LoadOutputFileFunctionType = (file: string) => string;
|
||||||
@ -267,3 +269,77 @@ function _generateSriAttributes(content: string) {
|
|||||||
|
|
||||||
return [{ name: 'integrity', value: `${algo}-${hash}` }];
|
return [{ name: 'integrity', value: `${algo}-${hash}` }];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ExtensionFilter = '.js' | '.css';
|
||||||
|
|
||||||
|
export interface WriteIndexHtmlOptions {
|
||||||
|
outputPath: string;
|
||||||
|
indexPath: string;
|
||||||
|
files?: EmittedFile[];
|
||||||
|
noModuleFiles?: EmittedFile[];
|
||||||
|
moduleFiles?: EmittedFile[];
|
||||||
|
baseHref?: string;
|
||||||
|
deployUrl?: string;
|
||||||
|
sri?: boolean;
|
||||||
|
scripts?: ExtraEntryPoint[];
|
||||||
|
styles?: ExtraEntryPoint[];
|
||||||
|
postTransform?: IndexHtmlTransform;
|
||||||
|
crossOrigin?: CrossOriginValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IndexHtmlTransform = (content: string) => Promise<string>;
|
||||||
|
|
||||||
|
export async function writeIndexHtml({
|
||||||
|
outputPath,
|
||||||
|
indexPath,
|
||||||
|
files = [],
|
||||||
|
noModuleFiles = [],
|
||||||
|
moduleFiles = [],
|
||||||
|
baseHref,
|
||||||
|
deployUrl,
|
||||||
|
sri = false,
|
||||||
|
scripts = [],
|
||||||
|
styles = [],
|
||||||
|
postTransform,
|
||||||
|
crossOrigin,
|
||||||
|
}: WriteIndexHtmlOptions) {
|
||||||
|
let content = readFileSync(indexPath).toString();
|
||||||
|
content = stripBom(content);
|
||||||
|
content = augmentIndexHtml({
|
||||||
|
input: outputPath,
|
||||||
|
inputContent: interpolateEnvironmentVariablesToIndex(content, deployUrl),
|
||||||
|
baseHref,
|
||||||
|
deployUrl,
|
||||||
|
crossOrigin,
|
||||||
|
sri,
|
||||||
|
entrypoints: generateEntryPoints({ scripts, styles }),
|
||||||
|
files: filterAndMapBuildFiles(files, ['.js', '.css']),
|
||||||
|
noModuleFiles: filterAndMapBuildFiles(noModuleFiles, '.js'),
|
||||||
|
moduleFiles: filterAndMapBuildFiles(moduleFiles, '.js'),
|
||||||
|
loadOutputFile: (filePath) =>
|
||||||
|
readFileSync(join(dirname(outputPath), filePath)).toString(),
|
||||||
|
});
|
||||||
|
if (postTransform) {
|
||||||
|
content = await postTransform(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFileSync(outputPath, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterAndMapBuildFiles(
|
||||||
|
files: EmittedFile[],
|
||||||
|
extensionFilter: ExtensionFilter | ExtensionFilter[]
|
||||||
|
): FileInfo[] {
|
||||||
|
const filteredFiles: FileInfo[] = [];
|
||||||
|
const validExtensions: string[] = Array.isArray(extensionFilter)
|
||||||
|
? extensionFilter
|
||||||
|
: [extensionFilter];
|
||||||
|
|
||||||
|
for (const { file, name, extension, initial } of files) {
|
||||||
|
if (name && initial && validExtensions.includes(extension)) {
|
||||||
|
filteredFiles.push({ file, extension, name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredFiles;
|
||||||
|
}
|
||||||
14
yarn.lock
14
yarn.lock
@ -23801,6 +23801,20 @@ stylus@0.54.8, stylus@^0.54.8:
|
|||||||
semver "^6.3.0"
|
semver "^6.3.0"
|
||||||
source-map "^0.7.3"
|
source-map "^0.7.3"
|
||||||
|
|
||||||
|
stylus@^0.55.0:
|
||||||
|
version "0.55.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.55.0.tgz#bd404a36dd93fa87744a9dd2d2b1b8450345e5fc"
|
||||||
|
integrity sha512-MuzIIVRSbc8XxHH7FjkvWqkIcr1BvoMZoR/oFuAJDlh7VSaNJzrB4uJ38GRQa+mWjLXODAMzeDe0xi9GYbGwnw==
|
||||||
|
dependencies:
|
||||||
|
css "^3.0.0"
|
||||||
|
debug "~3.1.0"
|
||||||
|
glob "^7.1.6"
|
||||||
|
mkdirp "~1.0.4"
|
||||||
|
safer-buffer "^2.1.2"
|
||||||
|
sax "~1.2.4"
|
||||||
|
semver "^6.3.0"
|
||||||
|
source-map "^0.7.3"
|
||||||
|
|
||||||
supports-color@^2.0.0:
|
supports-color@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user