liferay-npm-bundler-preset-reactjs
Version:
liferay npm bundler preset react
237 lines (190 loc) • 6.15 kB
text/typescript
/**
* SPDX-FileCopyrightText: © 2020 Liferay, Inc. <https://liferay.com>
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
import fs from 'fs-extra';
import {
AliasFromType,
AliasToType,
getAliasToType,
} from 'liferay-npm-build-tools-common/lib/alias';
import {
BundlerPluginParams,
BundlerTransformPluginState,
} from 'liferay-npm-build-tools-common/lib/api/plugins';
import FilePath from 'liferay-npm-build-tools-common/lib/file-path';
import PluginLogger from 'liferay-npm-build-tools-common/lib/plugin-logger';
import project from 'liferay-npm-build-tools-common/lib/project';
import {readAliases} from './config';
import {UnrolledAliasesMap, moduleExists, unrollAliasesMap} from './util';
/**
* Plugin entry point
*/
export default function (
params: BundlerPluginParams,
_state: BundlerTransformPluginState
): void {
const {log, pkg} = params;
const absRootDir = project.dir.join(pkg.dir);
const aliasesMap = readAliases(params, absRootDir);
const unrolledAliasesMap = unrollAliasesMap(aliasesMap);
reportAndResolveCollisions(log, absRootDir, unrolledAliasesMap);
processAliases(log, absRootDir, unrolledAliasesMap);
}
/**
*
* @param log
* @param absRootDir
* @param unrolledAliasesMap should be filtered so that there's only one alias
* per entry.
*/
function processAliases(
log: PluginLogger,
absRootDir: FilePath,
unrolledAliasesMap: UnrolledAliasesMap
) {
Object.entries(unrolledAliasesMap).forEach(
([absFromPath, unrolledAliases]) => {
// Sanity check
if (unrolledAliases.length > 1) {
throw new Error(
'Unrolled aliases map has unresolved collisions'
);
}
const alias = unrolledAliases[0];
const absFromFile = new FilePath(absFromPath);
const rootRelAbsDirPosixPath = absRootDir.relative(alias.absDir)
.asPosix;
const rootRelFilePosixPath = absRootDir.relative(absFromFile)
.asPosix;
switch (getAliasToType(alias.to)) {
case AliasToType.IGNORE: {
rewriteFile(
absFromFile,
`/* ignored by alias field(s) configured in ${rootRelAbsDirPosixPath} */`
);
log.info(
'replace-browser-modules',
`Emptied file '${rootRelFilePosixPath}' as ` +
`configured in '${rootRelAbsDirPosixPath}'`
).linkToCode(1);
break;
}
case AliasToType.EXTERNAL: {
rewriteFile(
absFromFile,
`/* redirected by alias field(s) in ${rootRelAbsDirPosixPath} */`,
`module.exports = require('${alias.to}');`
);
log.info(
'replace-browser-modules',
`Redirected file '${rootRelFilePosixPath}' to ` +
`'${alias.to}' as configured in ` +
`'${rootRelAbsDirPosixPath}'`
).linkToCode(2);
break;
}
case AliasToType.LOCAL: {
const absToFile = alias.absDir.join(
new FilePath(alias.to as string, {posix: true})
);
if (!absToFile.is(absFromFile)) {
const fromRelToFile = absFromFile
.dirname()
.relative(absToFile);
rewriteFile(
absFromFile,
`/* redirected by alias field(s) in ${rootRelAbsDirPosixPath} */`,
`module.exports = require('./${fromRelToFile.asPosix}');`
);
log.info(
'replace-browser-modules',
`Redirected file '${rootRelFilePosixPath}' to ` +
`'./${fromRelToFile.asPosix}' as configured in ` +
`'${rootRelAbsDirPosixPath}'`
).linkToCode(2);
}
break;
}
default:
break;
}
}
);
}
export function reportAndResolveCollisions(
log: PluginLogger,
absRootDir: FilePath,
unrolledAliasesMap: UnrolledAliasesMap
): void {
// Remove aliases out of ancestry line
Object.keys(unrolledAliasesMap).forEach((absFromPosixPath) => {
unrolledAliasesMap[absFromPosixPath] = unrolledAliasesMap[
absFromPosixPath
].filter((alias) => {
const included = absFromPosixPath.startsWith(alias.absDir.asPosix);
// No need to log anything because this type of alias must be
// addressed by rewriting requires, not by module rewrite
return included;
});
});
// Remove aliases of external modules that would overwrite a local one
Object.keys(unrolledAliasesMap).forEach((absFromPosixPath) => {
unrolledAliasesMap[absFromPosixPath] = unrolledAliasesMap[
absFromPosixPath
].filter((alias) => {
const included =
alias.fromType !== AliasFromType.EXTERNAL ||
!moduleExists(absFromPosixPath);
if (!included) {
const rootRelDir = absRootDir.relative(alias.absDir);
const where =
rootRelDir.asPosix === ''
? "project's root folder"
: `'${rootRelDir.asPosix}'`;
log.warn(
'replace-browser-modules',
`Alias '${alias.from}' configured in ${where} will not ` +
`be visible from outside because a local module with ` +
`the same name exists`
).linkToCode(3);
}
return included;
});
});
// Remove empty aliases
Object.keys(unrolledAliasesMap).forEach((absFromPath) => {
if (!unrolledAliasesMap[absFromPath].length) {
delete unrolledAliasesMap[absFromPath];
}
});
// Resolve collisions in multiple length aliases
Object.entries(unrolledAliasesMap)
.filter(([_absFromPath, unrolledAliases]) => unrolledAliases.length > 1)
.forEach(([absFromPath, unrolledAliases]) => {
// Sort by distance to absFromPath
unrolledAliases.sort(
(a, b) => a.absDir.asPosix.length - b.absDir.asPosix.length
);
// we always use the last
unrolledAliases.splice(0, unrolledAliases.length - 1);
const alias = unrolledAliases[0];
const rootRelFromPosixPath = absRootDir.relative(absFromPath)
.asPosix;
const rootRelDir = absRootDir.relative(alias.absDir);
const where =
rootRelDir.asPosix === ''
? "project's root folder"
: `'${rootRelDir.asPosix}'`;
log.warn(
'replace-browser-modules',
`File '${rootRelFromPosixPath}' is aliased more than once, ` +
`only the alias configured in ${where} will be visible ` +
`when required from outside`
).linkToCode(4);
});
}
function rewriteFile(absFile: FilePath, ...lines: string[]) {
fs.ensureDirSync(absFile.dirname().asNative);
fs.writeFileSync(absFile.asNative, lines.join('\n'));
}