@o3r/schematics
Version:
Schematics module of the Otter framework
185 lines • 9.66 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ngAddPackages = ngAddPackages;
exports.ngAddDependenciesRule = ngAddDependenciesRule;
const tslib_1 = require("tslib");
const node_fs_1 = require("node:fs");
const path = tslib_1.__importStar(require("node:path"));
const schematics_1 = require("@angular-devkit/schematics");
const tasks_1 = require("@angular-devkit/schematics/tasks");
const dependencies_1 = require("@schematics/angular/utility/dependencies");
const index_1 = require("../../utility/index");
const dependencies_2 = require("./dependencies");
const getNgAddSchema = (packageName, context) => {
try {
const collection = context.engine.createCollection(packageName);
return collection.createSchematic('ng-add');
}
catch {
context.logger.warn(`No ng-add found for ${packageName}`);
return undefined;
}
};
const sortDependencies = (packageJson, depType) => {
packageJson[depType] = packageJson[depType]
? Object.fromEntries(Object.entries(packageJson[depType] || {}).toSorted(([key1, _val1], [key2, _val2]) => key1.localeCompare(key2)))
: undefined;
};
/**
* Install via `ng add` a list of npm packages.
* @param packages List of packages to be installed via `ng add`
* @param options install options
* @param packageJsonPath path of the package json of the project where they will be installed
*/
function ngAddPackages(packages, options, packageJsonPath = '/package.json') {
if (packages.length === 0) {
return schematics_1.noop;
}
const cwd = process.cwd().replace(/[/\\]+/g, '/');
// FileSystem working directory might be different from Tree working directory (when using `yarn workspace` for example)
const fsWorkingDirectory = (options?.workingDirectory && !cwd.endsWith(options.workingDirectory)) ? options.workingDirectory : '.';
const versions = Object.fromEntries(packages.map((packageName, index) => [packageName, typeof options?.version === 'object' ? options.version[index] : options?.version]));
if (options?.workingDirectory && !packageJsonPath.startsWith(options.workingDirectory)) {
packageJsonPath = path.join(options.workingDirectory, packageJsonPath);
}
const getInstalledVersion = (packageName) => {
let versionFound;
for (const workingDirectory of new Set([fsWorkingDirectory, '.'])) {
try {
const fileSystemPackageJson = JSON.parse((0, node_fs_1.readFileSync)(path.join(workingDirectory, 'package.json'), { encoding: 'utf8' }));
let version;
if (options?.dependencyType === dependencies_1.NodeDependencyType.Dev) {
version = fileSystemPackageJson.devDependencies[packageName];
}
else if (options?.dependencyType === dependencies_1.NodeDependencyType.Peer) {
version = fileSystemPackageJson.peerDependencies[packageName];
}
else {
version = fileSystemPackageJson.dependencies[packageName];
}
if (versionFound && version !== versionFound) {
// In case of conflict between package.json files, we consider the package as not installed to force its update
return;
}
versionFound = version;
}
catch {
return;
}
}
return versionFound;
};
const getOptions = (schema) => {
const schemaOptions = schema.description.schemaJson?.properties || {};
return Object.entries(options || {}).reduce((accOptions, [key, value]) => {
if (schemaOptions[key]) {
accOptions[key] = value;
}
return accOptions;
}, {});
};
const installedVersions = packages.map((packageName) => getInstalledVersion(packageName));
const packageManager = (0, index_1.getPackageManager)();
const packagesToInstall = packages.filter((packageName, index) => !installedVersions[index] || installedVersions[index] !== versions[packageName]);
if (packagesToInstall.length === 0) {
return schematics_1.noop;
}
return (0, schematics_1.chain)([
// Update package.json in tree
(tree) => {
for (const filePath of new Set([packageJsonPath, './package.json'])) {
const packageJson = tree.readJson(filePath);
packages.forEach((packageName) => {
const version = versions[packageName] || 'latest';
if (options?.dependencyType === dependencies_1.NodeDependencyType.Dev) {
packageJson.devDependencies = { ...packageJson.devDependencies, [packageName]: version };
}
else if (options?.dependencyType === dependencies_1.NodeDependencyType.Peer) {
packageJson.peerDependencies = { ...packageJson.peerDependencies, [packageName]: version };
}
else {
packageJson.dependencies = { ...packageJson.dependencies, [packageName]: version };
}
});
['dependencies', 'devDependencies', 'peerDependencies'].forEach((depType) => {
sortDependencies(packageJson, depType);
});
tree.overwrite(filePath, JSON.stringify(packageJson, null, 2));
}
},
// Run ng-adds
async (tree, context) => {
if (options?.skipNgAddSchematicRun) {
context.logger.info(`Package(s) '${packagesToInstall.join(', ')}' was(were) installed.
The run of 'ng-add' schematics for the package(s) is intentionally skipped. You can do the run standalone, later.`);
return schematics_1.noop;
}
new Set([packageJsonPath, './package.json']).forEach((filePath) => {
(0, node_fs_1.mkdirSync)(path.join(process.cwd(), path.dirname(filePath)), { recursive: true });
(0, node_fs_1.writeFileSync)(path.join(process.cwd(), filePath), tree.readText(filePath));
});
context.addTask(new tasks_1.NodePackageInstallTask({
packageManager: packageManager,
hideOutput: false,
quiet: false
}));
await new Promise((resolve, reject) => context.engine.executePostTasks().subscribe({ next: () => { }, complete: () => resolve(), error: (errors) => reject(errors) }));
const ngAddsToApply = packagesToInstall
.map((packageName) => ({ packageName, ngAddCollection: getNgAddSchema(packageName, context) }))
.filter(({ packageName, ngAddCollection }) => {
if (!ngAddCollection) {
context.logger.info(`No ng-add schematic found for: '${packageName}'. Skipping ng add for: ${packageName}${versions[packageName] ? ' with version: ' + versions[packageName] : ''}`);
}
return !!ngAddCollection;
})
.map(({ packageName, ngAddCollection }) => (0, schematics_1.externalSchematic)(packageName, 'ng-add', getOptions(ngAddCollection)));
return (0, schematics_1.chain)(ngAddsToApply);
}
]);
}
/**
* Add dependencies to the project during ng add process
* @param options
* @param packageJsonPath
* @param deps
*/
function ngAddDependenciesRule(options, packageJsonPath, deps) {
return async (tree, context) => {
const { dependenciesToInstall = [], devDependenciesToInstall = [], additionalNgAddToRun = [] } = await (typeof deps === 'function' ? deps(options, tree, context) : deps);
const depsInfo = (0, index_1.getO3rPeerDeps)(packageJsonPath);
const workspaceProject = options.projectName ? (0, index_1.getWorkspaceConfig)(tree)?.projects[options.projectName] : undefined;
const projectDirectory = workspaceProject?.root || '.';
const projectPackageJson = tree.readJson(path.posix.join(projectDirectory, 'package.json'));
depsInfo.o3rPeerDeps.push(...additionalNgAddToRun.filter((dep) => !depsInfo.o3rPeerDeps.includes(dep)));
const internalDependencies = depsInfo.o3rPeerDeps.reduce((acc, dep) => {
acc[dep] = {
inManifest: [{
range: `${options.exactO3rVersion ? '' : '~'}${depsInfo.packageVersion}`,
types: (0, index_1.getProjectNewDependenciesTypes)(workspaceProject)
}],
ngAddOptions: { exactO3rVersion: options.exactO3rVersion }
};
return acc;
}, (0, dependencies_2.getPackageInstallConfig)(packageJsonPath, tree, options.projectName, false, !!options.exactO3rVersion));
const externalDependenciesInfo = (0, index_1.getExternalDependenciesInfo)({
devDependenciesToInstall,
dependenciesToInstall,
o3rPackageJsonPath: packageJsonPath,
projectType: workspaceProject?.projectType,
projectPackageJson
}, context.logger);
const dependencies = {
...internalDependencies,
...externalDependenciesInfo
};
return (0, schematics_1.chain)([
(0, dependencies_2.setupDependencies)({
projectName: options.projectName,
skipInstall: options.skipInstall,
dependencies,
ngAddToRun: depsInfo.o3rPeerDeps
})
]);
};
}
//# sourceMappingURL=ng-add-helpers.js.map