liferay-npm-bundler-preset-reactjs
Version:
liferay npm bundler preset react
179 lines (140 loc) • 3.98 kB
text/typescript
/**
* SPDX-FileCopyrightText: © 2020 Liferay, Inc. <https://liferay.com>
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
import prop from 'dot-prop';
import readJsonSync from 'read-json-sync';
import FilePath from './file-path';
/** Alias configuration as expressed in `package.json` files */
export type AliasConfig = false | string | AliasHash | undefined;
/** Value of the right hand side of an alias */
export type AliasToValue = false | string;
export enum AliasFromType {
EXTERNAL,
LOCAL,
}
export enum AliasToType {
IGNORE,
EXTERNAL,
LOCAL,
}
/**
* Index is the `from` field of the alias (`/` normalized to `./`), value is the
* `to` field.
*/
export interface AliasHash {
[index: string]: AliasToValue;
}
const aliasesCache: {[index: string]: AliasHash} = Object.create(null);
/**
* Get `resolve.aliasFields` configuration value.
*
* @remarks
* The `resolve.aliasFields` name resembles the value webpack's configuration
* uses for the same functionality (see
* https://webpack.js.org/configuration/resolve/#resolvealiasfields)
*/
export function getAliasFields(globalConfig: object, config: object): string[] {
let aliasFields: string[];
if (prop.has(config, 'resolve.aliasFields')) {
aliasFields = prop.get(config, 'resolve.aliasFields');
}
else if (prop.has(globalConfig, 'resolve.aliasFields')) {
aliasFields = prop.get(globalConfig, 'resolve.aliasFields');
}
else {
aliasFields = ['browser'];
}
return aliasFields;
}
/**
* Get the type of an alias `from` field (left hand side of alias)
*/
export function getAliasFromType(from: string): AliasFromType {
if (from.startsWith('/') || from.startsWith('.') || from.startsWith('..')) {
return AliasFromType.LOCAL;
}
return AliasFromType.EXTERNAL;
}
/**
* Get the type of an alias `to` field (right hand side of alias)
*/
export function getAliasToType(to: AliasToValue): AliasToType {
if (to === false) {
return AliasToType.IGNORE;
}
if (to.startsWith('/')) {
throw new Error(
`Absolute paths in require() calls are not supported: ${to}`
);
}
if (to.startsWith('.') || to.startsWith('..')) {
return AliasToType.LOCAL;
}
return AliasToType.EXTERNAL;
}
/**
* Load aliases from a `package.json` file.
*/
export function loadAliases(
pkgJsonFile: FilePath,
aliasFields: string[]
): AliasHash {
const absPkgJsonFile = pkgJsonFile.resolve();
const cacheKey = `${absPkgJsonFile.asNative}|${aliasFields.join()}`;
let aliases: AliasHash = aliasesCache[cacheKey];
// If not yet cached proceed
if (aliases === undefined) {
const pkgJson = safeReadJsonSync(absPkgJsonFile);
aliases = aliasFields.reduce(
(aliases: AliasHash, aliasField: string) => {
const aliasConfig: AliasConfig = pkgJson[aliasField];
if (aliasConfig === undefined) {
return aliases;
}
if (aliasConfig === false || typeof aliasConfig === 'string') {
let main = pkgJson['main'] || './index.js';
if (!main.startsWith('.')) {
main = `./${main}`;
}
aliases[main] = aliasConfig;
}
else {
Object.entries(aliasConfig).forEach(([from, to]) => {
aliases[from] = to;
});
}
return aliases;
},
{}
);
// Normalize `/` to `./`
Object.keys(aliases).forEach((key) => {
if (key.startsWith('/')) {
aliases[`.${key}`] = aliases[key];
delete aliases[key];
}
});
// Normalize local aliases to make them start with `./`
Object.keys(aliases).forEach((key) => {
if (key.startsWith('.')) {
const value = aliases[key];
if (typeof value === 'string' && !value.startsWith('.')) {
aliases[key] = `./${value}`;
}
}
});
}
// Store in cache
aliasesCache[cacheKey] = Object.assign(Object.create(null), aliases);
return aliases;
}
function safeReadJsonSync(pkgJsonFile: FilePath): object {
try {
return readJsonSync(pkgJsonFile.asNative);
}
catch (err) {
// Always return some sort of JSON if reading the file fails
return {};
}
}