@jsverse/transloco
Version:
The internationalization (i18n) library for Angular
181 lines • 9.07 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ngAdd = ngAdd;
const node_path_1 = require("node:path");
const schematics_1 = require("@angular-devkit/schematics");
const ng_ast_utils_1 = require("@schematics/angular/utility/ng-ast-utils");
const util_1 = require("@schematics/angular/utility/standalone/util");
const rules_1 = require("@schematics/angular/utility/standalone/rules");
const app_config_1 = require("@schematics/angular/utility/standalone/app_config");
const schematics_core_1 = require("../../schematics-core");
const schema_1 = require("./schema");
const http_loader_gen_1 = require("./generators/http-loader.gen");
const root_module_gen_1 = require("./generators/root-module.gen");
function updateEnvironmentBaseUrl(host, sourceRoot, defaultValue) {
const template = `$1{
baseUrl: '${defaultValue}',`;
(0, schematics_core_1.setEnvironments)(host, sourceRoot, (env) => env.indexOf('baseUrl') === -1
? env.replace(/(environment.*=*)\{/, template)
: env);
}
function resolveLoaderPath({ host, mainPath, isStandalone, modulePath, }) {
var _a;
let resolved = modulePath;
if (isStandalone) {
const bootstrapCall = (0, util_1.findBootstrapApplicationCall)(host, mainPath);
resolved =
((_a = (0, app_config_1.findAppConfig)(bootstrapCall, host, mainPath)) === null || _a === void 0 ? void 0 : _a.filePath) || mainPath;
resolved = (0, node_path_1.dirname)(resolved);
}
return resolved;
}
/**
* Checks whether a directory "exists" in the schematic Tree.
*
* In the Angular DevKit schematic virtual file system, directories are *not real entities* —
* only files exist. A directory is considered to "exist" if it has at least one file or
* subdirectory (that itself contains files) under its path.
*
* This function returns `true` if the given directory path contains any files or subdirectories,
* and `false` otherwise (including for empty or nonexistent directories).
*
* @param tree - The schematic virtual file system (Tree)
* @param dirPath - The path to check, e.g. '/src' or '/public'
*/
function dirExists(tree, dirPath) {
const dir = tree.getDir(dirPath);
return dir.subfiles.length > 0 || dir.subdirs.length > 0;
}
/**
* Detects the appropriate path for translation assets based on Angular project structure.
*
* Angular 18+ introduced a new project structure using a top-level `public/` directory instead
* of `src/assets/` for static assets. This function implements a three-tier detection strategy
* to determine where translation JSON files should be placed:
*
* 1. **Directory existence check**: If `/public` exists, assume Angular 18+ structure
* 2. **Legacy assets check**: If `${sourceRoot}/assets` exists, use traditional structure
* 3. **Version fallback**: Parse package.json @angular/core version as final determination
*
* The detection accounts for the fact that in schematics, we're working with a virtual file
* system where directories only "exist" if they contain files, and we need to make the right
* choice for where users expect their translation files to be placed.
*
* @param host - The schematic virtual file system (Tree)
* @param sourceRoot - The source root path (typically 'src' or 'projects/app-name/src')
* @returns The detected assets path, e.g. 'public/i18n/' or 'src/assets/i18n/'
*/
function detectAssetsPath(host, sourceRoot) {
var _a, _b;
if (dirExists(host, '/public')) {
return 'public/i18n/';
}
if (dirExists(host, `${sourceRoot}/assets`)) {
return `${sourceRoot}/assets/i18n/`;
}
// Fallback: Check package.json for Angular version
try {
const packageJson = JSON.parse(host.read('/package.json').toString());
const version = ((_a = packageJson.dependencies) === null || _a === void 0 ? void 0 : _a['@angular/core']) ||
((_b = packageJson.devDependencies) === null || _b === void 0 ? void 0 : _b['@angular/core']);
// Extract major version number from versions like "^18.2.0", "~17.0.0", ">=16.0.0", etc.
const majorVersionMatch = version === null || version === void 0 ? void 0 : version.match(/(\d+)\./);
const majorVersion = parseInt(majorVersionMatch[1]);
return majorVersion >= 18 ? 'public/i18n/' : `${sourceRoot}/assets/i18n/`;
}
catch (_c) {
return `${sourceRoot}/assets/i18n/`; // Safe default
}
}
function getUrlPathFromAssetsPath(assetsPath) {
if (assetsPath.startsWith('public/')) {
return assetsPath.replace('public/', '');
}
return assetsPath;
}
function ngAdd(options) {
return (host, context) => __awaiter(this, void 0, void 0, function* () {
var _a;
const langs = options.langs.split(',').map((l) => l.trim());
if (!options.project) {
throw new schematics_1.SchematicsException('Project name is required. You must explicitly provide the project name using --project=<project-name>');
}
const project = (0, schematics_core_1.getProject)(host, options.project);
const sourceRoot = (_a = project.sourceRoot) !== null && _a !== void 0 ? _a : 'src';
const isLib = project.projectType === 'library';
const assetsPath = options.path
? `${sourceRoot}/${options.path}`
: detectAssetsPath(host, sourceRoot);
const urlPath = getUrlPathFromAssetsPath(assetsPath);
const mainPath = yield (0, util_1.getMainFilePath)(host, options.project);
const isStandalone = (0, ng_ast_utils_1.isStandaloneApp)(host, mainPath);
const modulePath = isStandalone
? ''
: (0, node_path_1.dirname)((0, ng_ast_utils_1.getAppModulePath)(host, mainPath));
const actions = [];
if (options.loader === schema_1.Loaders.Http) {
const loaderPath = resolveLoaderPath({
host,
mainPath,
isStandalone,
modulePath,
});
if (isStandalone) {
actions.push((0, rules_1.addRootProvider)(options.project, ({ code, external }) => code `${external('provideHttpClient', '@angular/common/http')}()`));
}
else {
actions.push((0, rules_1.addRootImport)(options.project, ({ code, external }) => code `${external('HttpClientModule', '@angular/common/http')}`));
}
actions.push((0, schematics_1.mergeWith)((0, http_loader_gen_1.createLoaderFile)({
ssr: options.ssr,
loaderPath,
urlPath,
})));
}
const hasTranslationFiles = (0, schematics_core_1.checkIfTranslationFilesExist)(assetsPath, langs, '.json', true);
if (!hasTranslationFiles) {
actions.push((0, schematics_1.mergeWith)((0, schematics_core_1.createTranslateFiles)(langs, assetsPath)));
}
if (isStandalone) {
actions.push((0, rules_1.addRootProvider)(options.project, ({ code, external }) => {
external('isDevMode', '@angular/core');
external('TranslocoHttpLoader', './transloco-loader');
return code `${external('provideTransloco', '@jsverse/transloco')}({
config: {
availableLangs: [${(0, schematics_core_1.stringifyList)(langs)}],
defaultLang: '${langs[0]}',
// Remove this option if your application doesn't support changing language in runtime.
reRenderOnLangChange: true,
prodMode: !isDevMode(),
},
loader: TranslocoHttpLoader
})`;
}));
}
else {
actions.push((0, rules_1.addRootImport)(options.project, ({ code, external }) => code `${external('TranslocoRootModule', './transloco-root.module')}`), (0, schematics_1.mergeWith)((0, root_module_gen_1.createTranslocoModule)({
sourceRoot,
isLib,
ssr: options.ssr,
langs,
modulePath,
host,
})));
}
if (options.ssr) {
updateEnvironmentBaseUrl(host, sourceRoot, 'http://localhost:4200');
}
(0, schematics_core_1.createGlobalConfig)(host, langs, assetsPath);
return (0, schematics_1.chain)(actions)(host, context);
});
}
//# sourceMappingURL=index.js.map