Skip to content

Commit

Permalink
feat(stark-build): adapt build utils and webpack config to read globa…
Browse files Browse the repository at this point in the history
…l styles from angular.json to align with Angular CLI

ISSUES CLOSED: NationalBankBelgium#1070

BREAKING CHANGE: global styles must be included in the angular.json (standard Angular CLI behavior) instead of importing them directly in the app
  • Loading branch information
christophercr committed Jan 23, 2019
1 parent e8c2e0b commit b1f0e22
Show file tree
Hide file tree
Showing 9 changed files with 241 additions and 34 deletions.
76 changes: 69 additions & 7 deletions packages/stark-build/config/build-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ const ANGULAR_APP_CONFIG = {
deployUrl: angularCliAppConfig.architect.build.options.deployUrl || "",
baseHref: angularCliAppConfig.architect.build.options.baseHref || "",
sourceRoot: angularCliAppConfig.sourceRoot,
outputPath: angularCliAppConfig.architect.build.options.outputPath
outputPath: angularCliAppConfig.architect.build.options.outputPath,
buildOptions: angularCliAppConfig.architect.build.options || {}
};

const DEFAULT_METADATA = {
Expand Down Expand Up @@ -41,6 +42,65 @@ function readTsConfig(tsConfigPath) {
return ts.parseJsonConfigFileContent(configResult.config, ts.sys, path.dirname(tsConfigPath), undefined, tsConfigPath);
}

/**
* Logic extracted from @angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/styles.js
*
* @returns {{entryPoints: {}, globalStylePaths: Array}}
*/
function getApplicationGlobalStylesConfig() {
const stylesConfig = { entryPoints: {}, globalStylePaths: [] };

if (ANGULAR_APP_CONFIG.buildOptions.styles.length > 0) {
// const chunkNames = [];
ngCliUtils.normalizeExtraEntryPoints(ANGULAR_APP_CONFIG.buildOptions.styles, "styles").forEach(style => {
const resolvedPath = path.resolve(ANGULAR_APP_CONFIG.config.root, style.input);
// Add style entry points.
if (stylesConfig.entryPoints[style.bundleName]) {
stylesConfig.entryPoints[style.bundleName].push(resolvedPath);
} else {
stylesConfig.entryPoints[style.bundleName] = [resolvedPath];
}
/// Add lazy styles to the list.
/// TODO not used for the moment
/// if (style.lazy) {
/// chunkNames.push(style.bundleName);
/// }
/// Add global css paths.
stylesConfig.globalStylePaths.push(resolvedPath);
});
/// TODO not used for the moment
/// if (chunkNames.length > 0) {
/// // Add plugin to remove hashes from lazy styles.
/// extraPlugins.push(new webpack_1.RemoveHashPlugin({ chunkNames, hashFormat }));
/// }
}

return stylesConfig;
}

/**
* Method from @angular-devkit/build-angular/src/angular-cli-files/utilities/package-chunk-sort.js
* @returns {string[]}
*/
function generateEntryPoints() {
let entryPoints = ["polyfills", "sw-register"];
// Add all styles/scripts, except lazy-loaded ones.
[
...Object.keys(getApplicationGlobalStylesConfig().entryPoints)
/// TODO not used for the moment
/// ...ngCliUtils.normalizeExtraEntryPoints(appConfig.scripts, 'scripts')
/// .filter(entry => !entry.lazy)
/// .map(entry => entry.bundleName),
].forEach(bundleName => {
if (entryPoints.indexOf(bundleName) === -1) {
entryPoints.push(bundleName);
}
});

entryPoints.push("main");
return entryPoints;
}

/**
* Read the content of angular.json to get the path of the environment file.
* It returns the path of the replacement file defined in "fileReplacements" of the environment or the default file
Expand Down Expand Up @@ -136,10 +196,10 @@ function getNbbAssetsConfig() {
* See: https://github.com/angular/angular-cli/wiki/angular-workspace
*/
function getApplicationAssetsConfig() {
const appConfig = ANGULAR_APP_CONFIG.config;
const buildOptions = ANGULAR_APP_CONFIG.buildOptions;

if (appConfig.architect && appConfig.architect.build && appConfig.architect.build.options && appConfig.architect.build.options.assets) {
return getCopyWebpackPluginConfig(appConfig.architect.build.options.assets);
if (buildOptions.assets && buildOptions.assets instanceof Array) {
return getCopyWebpackPluginConfig(buildOptions.assets);
}

return [];
Expand Down Expand Up @@ -212,8 +272,10 @@ function getCopyWebpackPluginConfig(assets) {

exports.ANGULAR_APP_CONFIG = ANGULAR_APP_CONFIG;
exports.DEFAULT_METADATA = DEFAULT_METADATA;
exports.supportES2015 = supportES2015;
exports.readTsConfig = readTsConfig;
exports.generateEntryPoints = generateEntryPoints;
exports.getApplicationAssetsConfig = getApplicationAssetsConfig;
exports.getApplicationGlobalStylesConfig = getApplicationGlobalStylesConfig;
exports.getEnvironmentFile = getEnvironmentFile;
exports.getNbbAssetsConfig = getNbbAssetsConfig;
exports.getApplicationAssetsConfig = getApplicationAssetsConfig;
exports.readTsConfig = readTsConfig;
exports.supportES2015 = supportES2015;
149 changes: 149 additions & 0 deletions packages/stark-build/config/ng-cli-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,24 @@ const fs = require("fs");
const cliUtilConfig = require("@angular/cli/utilities/config");
const { formatDiagnostics } = require("@angular/compiler-cli/ngtools2");

/**
* The Separator for normalized path.
* @type {string}
*/
const normalizedSep = "/";

/**
* The root of a normalized path.
* @type {string}
*/
const normalizedRoot = normalizedSep;

/**
* normalize() cache to reduce computation. For now this grows and we never flush it, but in the
* future we might want to add a few cache flush to prevent this from growing too large.
*/
let normalizedCache = new Map();

function isDirectory(pathToCheck) {
try {
return fs.statSync(pathToCheck).isDirectory();
Expand Down Expand Up @@ -63,8 +81,139 @@ function getWorkspace() {
return cliUtilConfig.getWorkspace();
}

/**
* Code taken from @angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/utils.js
*
* @param extraEntryPoints
* @param defaultBundleName
* @returns {*}
*/
function normalizeExtraEntryPoints(extraEntryPoints, defaultBundleName) {
return extraEntryPoints.map(entry => {
let normalizedEntry;
if (typeof entry === "string") {
normalizedEntry = { input: entry, lazy: false, bundleName: defaultBundleName };
} else {
let bundleName;
if (entry.bundleName) {
bundleName = entry.bundleName;
} else if (entry.lazy) {
// Lazy entry points use the file name as bundle name.
bundleName = basename(normalize(entry.input.replace(/\.(js|css|scss|sass|less|styl)$/i, "")));
} else {
bundleName = defaultBundleName;
}
normalizedEntry = Object.assign({}, entry, { bundleName });
}
return normalizedEntry;
});
}

/**
* Code taken from @angular-devkit/core/src/virtual-fs/path.js
*
* Return the basename of the path, as a Path. See path.basename
*/
function basename(path) {
const i = path.lastIndexOf(normalizedSep);
if (i === -1) {
return fragment(path);
} else {
return fragment(path.substr(path.lastIndexOf(normalizedSep) + 1));
}
}

/**
* Code taken from @angular-devkit/core/src/virtual-fs/path.js
*/
function fragment(path) {
if (path.indexOf("/") !== -1) {
throw new Exception(path);
}
return path;
}

/**
* Code taken from @angular-devkit/core/src/virtual-fs/path.js
*
* Normalize a string into a Path. This is the only mean to get a Path type from a string that
* represents a system path. This method cache the results as real world paths tend to be
* duplicated often.
* Normalization includes:
* - Windows backslashes `\\` are replaced with `/`.
* - Windows drivers are replaced with `/X/`, where X is the drive letter.
* - Absolute paths starts with `/`.
* - Multiple `/` are replaced by a single one.
* - Path segments `.` are removed.
* - Path segments `..` are resolved.
* - If a path is absolute, having a `..` at the start is invalid (and will throw).
* @param path The path to be normalized.
*/
function normalize(path) {
let maybePath = normalizedCache.get(path);
if (!maybePath) {
maybePath = noCacheNormalize(path);
normalizedCache.set(path, maybePath);
}
return maybePath;
}

/**
* Code taken from @angular-devkit/core/src/virtual-fs/path.js
*
* The no cache version of the normalize() function. Used for benchmarking and testing.
*/
function noCacheNormalize(path) {
if (path == "" || path == ".") {
return "";
} else if (path == normalizedRoot) {
return normalizedRoot;
}
// Match absolute windows path.
const original = path;
if (path.match(/^[A-Z]:[\/\\]/i)) {
path = "\\" + path[0] + "\\" + path.substr(3);
}
// We convert Windows paths as well here.
const p = path.split(/[\/\\]/g);
let relative = false;
let i = 1;
// Special case the first one.
if (p[0] != "") {
p.unshift(".");
relative = true;
}
while (i < p.length) {
if (p[i] == ".") {
p.splice(i, 1);
} else if (p[i] == "..") {
if (i < 2 && !relative) {
throw new Error(`Path ${JSON.stringify(original)} is invalid.`);
} else if (i >= 2 && p[i - 1] != "..") {
p.splice(i - 1, 2);
i--;
} else {
i++;
}
} else if (p[i] == "") {
p.splice(i, 1);
} else {
i++;
}
}
if (p.length == 1) {
return p[0] == "" ? normalizedSep : "";
} else {
if (p[0] == ".") {
p.shift();
}
return p.join(normalizedSep);
}
}

exports.getAngularCliAppConfig = getAngularCliAppConfig;
exports.getDirectoriesNames = getDirectoriesNames;
exports.getWorkspace = getWorkspace;
exports.isDirectory = isDirectory;
exports.normalizeExtraEntryPoints = normalizeExtraEntryPoints;
exports.validateAngularCLIConfig = validateAngularCLIConfig;
20 changes: 12 additions & 8 deletions packages/stark-build/config/webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@ module.exports = options => {
const METADATA = Object.assign({}, buildUtils.DEFAULT_METADATA, options.metadata || {});
const supportES2015 = buildUtils.supportES2015(METADATA.TS_CONFIG_PATH);

const entry = {
polyfills: "./src/polyfills.browser.ts",
main: "./src/main.browser.ts"
};
const globalStylePaths = buildUtils.getApplicationGlobalStylesConfig().globalStylePaths;

const entry = Object.assign({}, buildUtils.getApplicationGlobalStylesConfig().entryPoints, {
polyfills: helpers.root(buildUtils.ANGULAR_APP_CONFIG.buildOptions.polyfills),
main: helpers.root(buildUtils.ANGULAR_APP_CONFIG.buildOptions.main)
});

const tsConfigApp = buildUtils.readTsConfig(helpers.root(METADATA.TS_CONFIG_PATH));

Expand Down Expand Up @@ -225,7 +227,7 @@ module.exports = options => {
}
}
],
exclude: [helpers.root(buildUtils.ANGULAR_APP_CONFIG.sourceRoot, "styles")]
exclude: globalStylePaths
},

/**
Expand Down Expand Up @@ -257,7 +259,7 @@ module.exports = options => {
},
"sass-loader"
],
exclude: [helpers.root(buildUtils.ANGULAR_APP_CONFIG.sourceRoot, "styles")]
exclude: globalStylePaths
},

/**
Expand Down Expand Up @@ -288,7 +290,7 @@ module.exports = options => {
}
}
],
exclude: [helpers.root(buildUtils.ANGULAR_APP_CONFIG.sourceRoot, "styles")]
exclude: globalStylePaths
},

/**
Expand Down Expand Up @@ -428,7 +430,9 @@ module.exports = options => {
template: "src/index.html",
title: METADATA.TITLE,
chunksSortMode: function(a, b) {
const entryPoints = ["inline", "polyfills", "sw-register", "styles", "vendor", "main"];
// generated entry points will include those from styles config
// logic extracted from getBrowserConfig function in @angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/browser.js
const entryPoints = buildUtils.generateEntryPoints();
return entryPoints.indexOf(a.names[0]) - entryPoints.indexOf(b.names[0]);
},
metadata: METADATA,
Expand Down
8 changes: 5 additions & 3 deletions packages/stark-build/config/webpack.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ module.exports = function(env) {
// PUBLIC: process.env.PUBLIC_DEV || HOST + ':' + PORT // TODO check if needed/useful in our case?
});

const globalStylePaths = buildUtils.getApplicationGlobalStylesConfig().globalStylePaths;

// Directives to be used in CSP header
const cspDirectives = [
"base-uri 'self'",
Expand Down Expand Up @@ -180,7 +182,7 @@ module.exports = function(env) {
}
}
],
include: [helpers.root(buildUtils.ANGULAR_APP_CONFIG.sourceRoot, "styles")]
include: globalStylePaths
},

/**
Expand Down Expand Up @@ -215,7 +217,7 @@ module.exports = function(env) {
},
"sass-loader"
],
include: [helpers.root(buildUtils.ANGULAR_APP_CONFIG.sourceRoot, "styles")]
include: globalStylePaths
},

/**
Expand Down Expand Up @@ -246,7 +248,7 @@ module.exports = function(env) {
}
}
],
include: [helpers.root(buildUtils.ANGULAR_APP_CONFIG.sourceRoot, "styles")]
include: globalStylePaths
}
]
},
Expand Down
7 changes: 4 additions & 3 deletions packages/stark-build/config/webpack.prod.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ module.exports = function() {
});
const isCITestEnv = helpers.hasProcessFlag("ci-test-env");
const supportES2015 = buildUtils.supportES2015(METADATA.TS_CONFIG_PATH);
const globalStylePaths = buildUtils.getApplicationGlobalStylesConfig().globalStylePaths;

const webpackConfig = webpackMerge(commonConfig({ ENV: ENV, metadata: METADATA }), {
/**
Expand Down Expand Up @@ -220,7 +221,7 @@ module.exports = function() {
}
}
],
include: [helpers.root(buildUtils.ANGULAR_APP_CONFIG.sourceRoot, "styles")]
include: globalStylePaths
},

/**
Expand Down Expand Up @@ -250,7 +251,7 @@ module.exports = function() {
},
"sass-loader"
],
include: [helpers.root(buildUtils.ANGULAR_APP_CONFIG.sourceRoot, "styles")]
include: globalStylePaths
},

/**
Expand Down Expand Up @@ -279,7 +280,7 @@ module.exports = function() {
}
}
],
include: [helpers.root(buildUtils.ANGULAR_APP_CONFIG.sourceRoot, "styles")]
include: globalStylePaths
}
]
},
Expand Down
Loading

0 comments on commit b1f0e22

Please sign in to comment.