feat: support workspace configs

This commit is contained in:
Igor Randjelovic
2021-03-11 16:19:05 +01:00
parent 567a7ed25c
commit 437c3b41f9
12 changed files with 410 additions and 62 deletions

View File

@@ -1,15 +1,20 @@
import Config from 'webpack-chain';
import path from 'path';
import { resolve, dirname } from 'path';
import { existsSync } from 'fs';
import get from 'lodash.get'
import { getProjectRootPath } from '../helpers/project';
import { getProjectFilePath, getProjectRootPath } from '../helpers/project';
import { getEntryPath, getPlatformName } from '../helpers/platform';
import { env as _env, IWebpackEnv } from '../index';
import { getEntryPath } from '../helpers/platform';
import { addCopyRule } from "../helpers/copyRules";
import base from './base';
export default function (config: Config, env: IWebpackEnv = _env): Config {
base(config, env);
const tsConfigPath = path.join(getProjectRootPath(), 'tsconfig.json');
const tsConfigPath = getProjectFilePath('tsconfig.json')
applyFileReplacements(config)
// remove default ts rule
config.module.rules.delete('ts');
@@ -54,3 +59,122 @@ function getAngularCompilerPlugin() {
const { AngularCompilerPlugin } = require('@ngtools/webpack');
return AngularCompilerPlugin;
}
/**
* @internal exported for tests
*/
export function applyFileReplacements(
config,
fileReplacements = getFileReplacementsFromWorkspaceConfig()
) {
if (!fileReplacements) {
return
}
Object.entries(fileReplacements).forEach(([_replace, _with]) => {
// in case we are replacing source files - we'll use aliases
if (_replace.match(/\.ts$/)) {
return config.resolve.alias.set(_replace, _with)
}
// otherwise we will override the replaced file with the replacement
addCopyRule({
from: _with, // copy the replacement file
to: _replace, // to the original "to-be-replaced" file
force: true,
})
})
}
// todo: move into project helper if used elsewhere
// todo: write tests
function findFile(fileName, currentDir): string | null {
// console.log(`findFile(${fileName}, ${currentDir})`)
const path = resolve(currentDir, fileName);
if (existsSync(path)) {
return path
}
// bail if we reached the root dir
if (currentDir === resolve('/')) {
return null
}
// traverse to the parent folder
return findFile(fileName, resolve(currentDir, '..'))
}
function findWorkspaceConfig(): string {
const possibleConfigNames = [
'angular.json',
'workspace.json'
]
for (const name of possibleConfigNames) {
const path = findFile(name, getProjectRootPath());
if (path) {
return path
}
}
// not found
return null;
}
interface IWorkspaceConfigFileReplacement {
replace: string,
with: string
}
interface IReplacementMap {
[from: string]: string
}
/**
* @internal exported for tests
*/
export function getFileReplacementsFromWorkspaceConfig(
configPath: string = findWorkspaceConfig()
): IReplacementMap | null {
const platform = getPlatformName();
if (!_env.configuration || !_env.projectName) {
return null;
}
const configurations = _env.configuration.split(',').map(c => c.trim())
if (!configPath || configPath === '') {
return null;
}
const config = require(configPath);
const project = get(config, `projects.${_env.projectName}`)
const targetProp = project?.architect ? 'architect' : 'targets';
if (!project) {
return null;
}
const replacements = {};
const setReplacement = (entry: IWorkspaceConfigFileReplacement) => {
const relativeReplace = resolve(dirname(configPath), entry.replace)
const relativeWith = resolve(dirname(configPath), entry.with)
replacements[relativeReplace] = relativeWith
}
configurations.forEach(configuration => {
const defaultReplacements = get(project, `${targetProp}.default.configurations.${configuration}.fileReplacements`)
const platformReplacements = get(project, `${targetProp}.${platform}.configurations.${configuration}.fileReplacements`)
// add default replacements
defaultReplacements?.map(setReplacement)
// add platform replacements (always override defaults!)
platformReplacements?.map(setReplacement)
})
return replacements;
}

View File

@@ -12,17 +12,29 @@ import { env } from '..';
export let copyRules = new Set([]);
/**
* Utility to add new copy rules. Accepts a glob. For example
* @internal
*/
export let additionalCopyRules = []
/**
* Utility to add new copy rules. Accepts a glob or an object. For example
* - **\/*.html - copy all .html files found in any sub dir.
* - myFolder/* - copy all files from myFolder
*
* When passing an object - no additional processing is done, and it's
* applied as-is. Make sure to set every required property.
*
* The path is relative to the folder of the entry file
* (specified in the main field of the package.json)
*
* @param {string} glob
* @param {string|object} globOrObject
*/
export function addCopyRule(glob: string) {
copyRules.add(glob);
export function addCopyRule(globOrObject: string | object) {
if(typeof globOrObject === 'string') {
return copyRules.add(globOrObject);
}
additionalCopyRules.push(globOrObject)
}
/**
@@ -65,7 +77,7 @@ export function applyCopyRules(config: Config) {
context: entryDir,
noErrorOnMissing: true,
globOptions,
})),
})).concat(additionalCopyRules),
},
]);
}

View File

@@ -15,7 +15,7 @@ export function applyDotEnvPlugin(config: Config) {
config.when(path !== null, (config) => {
config.plugin('DotEnvPlugin').use(DotEnvPlugin, [
{
path: getDotEnvPath(),
path,
silent: true, // hide any errors
},
]);

View File

@@ -25,6 +25,10 @@ export interface IWebpackEnv {
// for custom platforms
platform?: string;
// angular specific
configuration?: string;
projectName?: string;
production?: boolean;
report?: boolean;
hmr?: boolean;