@gasket/plugin-intl
Version:
NodeJS script to build localization files.
213 lines (212 loc) • 9.16 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
BuildModules: function() {
return BuildModules;
},
/**
* Discovers locale files under node modules with and copies them to output dir.
* @param {import("@gasket/core").Gasket} gasket - Gasket API
*/ default: function() {
return buildModules;
}
});
const _fsextra = /*#__PURE__*/ _interop_require_default(require("fs-extra"));
const _path = /*#__PURE__*/ _interop_require_default(require("path"));
const _fsutils = require("./utils/fs-utils.cjs");
const _configureutils = require("./utils/configure-utils.cjs");
const _debug = /*#__PURE__*/ _interop_require_default(require("debug"));
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
const debugLog = (0, _debug.default)('gasket:plugin:intl:buildModules');
const rePkgParts = RegExp("^(?<name>(?:@[\\w-]+\\/)?[\\w-]+)(?<dir>\\/[\\w-]+)?$");
class BuildModules {
/**
* Given a source folder, this function minifies all the files in that folder
* and sets a unique hash for each file and saves in the target location
* @param {string} srcDir - Source directory path
* @param {string} tgtDir - Target directory path
* @returns {Promise} promise
*/ async copyFolder(srcDir, tgtDir) {
debugLog(`Copying folder from ${srcDir} to ${tgtDir}`);
const fileNames = await _fsextra.default.readdir(srcDir);
const promises = fileNames.map(async (fileName)=>{
const srcFile = _path.default.join(srcDir, fileName);
const tgtFile = _path.default.join(tgtDir, fileName);
if (_path.default.extname(srcFile) === '.json') {
return await this.copyFile(srcFile, tgtFile);
}
});
return await Promise.all(promises);
}
/**
* Copies the source file to proper target location
* @param {string} src - full path to source file
* @param {string} tgt - target folder location
* @returns {Promise} - resolves once the file is saved
*/ async copyFile(src, tgt) {
debugLog(`Copying file from ${src} to ${tgt}`);
await _fsextra.default.mkdirp(_path.default.dirname(tgt));
const buffer = await _fsextra.default.readFile(src);
const output = JSON.parse(buffer);
return (0, _fsutils.saveJsonFile)(tgt, output);
}
/**
* Processes locale files from source to target build directory
* @param {string} srcDir - Source locale directory
* @param {string} tgtDir - Target locale directory
* @param {string[]} fileNames - Names of the locale files
* @returns {Promise} promise
*/ processFiles(srcDir, tgtDir, fileNames) {
debugLog(`Processing files in ${srcDir} to target ${tgtDir}`);
const promises = fileNames.map(async (fileName)=>{
const srcFile = _path.default.join(srcDir, fileName);
const tgtFile = _path.default.join(tgtDir, fileName);
if (_path.default.extname(srcFile) === '.json') {
return await this.copyFile(srcFile, tgtFile);
} else if ((await _fsextra.default.lstat(srcFile)).isDirectory()) {
return await this.copyFolder(srcFile, tgtFile);
}
});
return Promise.all(promises);
}
/**
* Reads the source directory and returns the package name e.g. @gasket/next
* @param {string} srcDir - Source directory path
* @returns {string} package name
*/ getPackageNameFromDir(srcDir) {
const leafFolder = _path.default.dirname(srcDir);
const leafFolderName = _path.default.basename(leafFolder);
const parentFolder = _path.default.dirname(leafFolder);
const parentFolderName = _path.default.basename(parentFolder);
let pkgName = leafFolderName;
if (parentFolderName.startsWith('@')) {
pkgName = `${parentFolderName}/${leafFolderName}`;
}
return pkgName;
}
/**
* Reads the package.json and returns the package name e.g. @gasket/next
* @param {string} srcDir - Source directory path (a locales directory)
* @returns {Promise<string>} package name
*/ async getPackageName(srcDir) {
const pkgDir = _path.default.dirname(srcDir);
try {
const pkg = await _fsextra.default.readJson(_path.default.join(pkgDir, 'package.json'));
return pkg.name || this.getPackageNameFromDir(srcDir);
} catch {
return this.getPackageNameFromDir(srcDir);
}
}
/**
* Processes directories
* @param {import('./internal.d.ts').SrcPkgDir[]} srcPkgDirs - list of dirs to process
*/ async processDirs(srcPkgDirs) {
for (const [pkgName, srcDir] of srcPkgDirs){
const tgtDir = _path.default.join(this._outputDir, pkgName);
this._logger.info(`build:locales: Updating locale files for: ${pkgName}`);
await _fsextra.default.remove(tgtDir);
await _fsextra.default.mkdirp(tgtDir);
const fileNames = await _fsextra.default.readdir(srcDir);
await this.processFiles(srcDir, tgtDir, fileNames);
}
this._logger.info(`build:locales: Completed locale files update.`);
}
/**
* Find modules that have /locales folder to process
* @returns {Promise<import('./internal.d.ts').SrcPkgDir[]>} source package directories
*/ async discoverDirs() {
/** @type {import('./internal.d.ts').SrcPkgDir[]} */ const results = [];
for await (const [pkgName, dir] of (0, _fsutils.getPackageDirs)(this._nodeModulesDir)){
if (!this._excludes.includes(_path.default.basename(dir))) {
const buildDir = _path.default.resolve(_path.default.join(dir, ...this._lookupDir.split('/')));
try {
const stat = await _fsextra.default.lstat(buildDir);
if (stat.isDirectory()) {
results.push([
pkgName,
buildDir
]);
}
} catch {
// ignore
}
}
}
return results;
}
/**
* Find modules with locale directories to process
* @returns {Promise<import('./internal.d.ts').SrcPkgDir[]>} source package directories
*/ async gatherModuleDirs() {
if (this._lookupModuleDirs) {
const promises = this._lookupModuleDirs.map(async (lookupDir)=>{
const match = lookupDir.match(rePkgParts);
const pkgName = match?.groups?.name;
if (!pkgName) {
this._logger.warn(`build:locales: malformed module name: ${lookupDir}`);
return;
}
const subDir = (match.groups.dir ?? '/locales').substring(1);
const buildDir = _path.default.join(this._nodeModulesDir, ...pkgName.split('/'), ...subDir.split('/'));
try {
const stat = await _fsextra.default.lstat(buildDir);
if (stat.isDirectory()) {
return [
pkgName,
buildDir
];
}
} catch {
// skip
}
this._logger.warn(`build:locales: locales directory not found for: ${lookupDir}`);
});
const results = (await Promise.all(promises)).filter(Boolean);
return /** @type {import('./internal.d.ts').SrcPkgDir[]} */ results;
}
return this.discoverDirs();
}
/**
* Starts the build process
*/ async run() {
await _fsextra.default.remove(this._outputDir);
await _fsextra.default.mkdirp(this._outputDir);
const srcPkgDirs = await this.gatherModuleDirs();
await this.processDirs(srcPkgDirs);
}
/**
* Instantiate a builder to gather locale files
* @param {import("@gasket/core").Gasket} gasket - Gasket API
*/ constructor(gasket){
const { logger, config: { root } } = gasket;
const intlConfig = (0, _configureutils.getIntlConfig)(gasket);
const { modules } = intlConfig;
if (Array.isArray(modules)) {
this._lookupModuleDirs = modules;
}
/** @type {Record<string, any>} */ let excludes, localesDir;
if (typeof modules === 'object' && !Array.isArray(modules)) {
({ excludes, localesDir } = modules);
}
this._logger = logger;
this._outputDir = _path.default.resolve(_path.default.join(intlConfig.localesDir, 'modules'));
this._nodeModulesDir = _path.default.resolve(_path.default.join(root, 'node_modules'));
this._lookupDir = localesDir;
this._excludes = excludes;
}
}
async function buildModules(gasket) {
const builder = new BuildModules(gasket);
await builder.run();
}