@nxextensions/firebase-cypress
Version:
An NX Plugin for Firebase Applications that would like to use emulators for E2E testing with Cypress
230 lines • 11.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.initGenerator = initGenerator;
const tslib_1 = require("tslib");
const devkit_1 = require("@nx/devkit");
const add_plugin_1 = require("@nx/devkit/src/utils/add-plugin");
const process = tslib_1.__importStar(require("node:process"));
const js_1 = require("@nx/js");
const target_generator_1 = require("../../target-generator");
const path_1 = require("path");
const cypress_version_1 = require("../../utils/cypress-version");
const config_1 = require("../../utils/config");
const child_process_1 = require("child_process");
const firebaseJsonGlob = '**/firebase.json';
function normalizeOptions(options, project, tree) {
var _a, _b, _c, _d, _e;
options !== null && options !== void 0 ? options : (options = {});
(_a = options.directory) !== null && _a !== void 0 ? _a : (options.directory = 'src');
(_b = options.js) !== null && _b !== void 0 ? _b : (options.js = false);
(_c = options.jsx) !== null && _c !== void 0 ? _c : (options.jsx = false);
const offsetFromProjectRoot = options.directory.split('/')
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.map(_ => '..')
.join('/');
options.hasTsConfig = tree.exists((0, devkit_1.joinPathFragments)(project.root, 'tsconfig.json'));
(_d = options.bundler) !== null && _d !== void 0 ? _d : (options.bundler = 'webpack');
(_e = options.baseUrl) !== null && _e !== void 0 ? _e : (options.baseUrl = 'http://localhost:4200');
return {
...options,
offsetFromProjectRoot: `${offsetFromProjectRoot}/`,
projectConfig: project
};
}
async function initGenerator(tree, options) {
var _a, _b;
// set plugin options
const nxJson = (0, devkit_1.readNxJson)(tree);
(_a = options.skipNxFirebase) !== null && _a !== void 0 ? _a : (options.skipNxFirebase = true);
const addPlugins = options.addPlugin = process.env.NX_ADD_PLUGINS !== 'false' &&
nxJson.useInferencePlugins !== false;
const graph = await (0, devkit_1.createProjectGraphAsync)({ exitOnError: true });
let cp = undefined;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (!((_b = nxJson.plugins) === null || _b === void 0 ? void 0 : _b.find((x) => x.plugin === '@nxextensions/nx-firebase')) && !options.skipNxFirebase) {
cp = (0, child_process_1.spawn)('npx nx add @nxextensions/nx-firebase --verbose', {
detached: false,
shell: true
});
cp.stdout.on('data', (data) => {
process.stdout.write(data);
});
cp.stderr.on('data', (data) => {
process.stderr.write(data);
});
cp.on('error', (err) => {
process.stderr.write(err.toString());
});
}
if (addPlugins) {
await addPlugin(tree, graph, true);
}
// detect projects
const applicationProjectNames = [];
Object.keys(graph.nodes).forEach((p) => {
if (graph.nodes[p].type === 'app') {
applicationProjectNames.push(graph.nodes[p].name);
}
});
process.stdout.write(`Found the following projects: ${applicationProjectNames.join(', ')}\r\n`);
const projects = (0, devkit_1.readProjectsConfigurationFromProjectGraph)(graph);
const applicationProjects = Object.keys(projects.projects).map((key) => applicationProjectNames.includes(key) && projects.projects[key]).filter(x => typeof x === 'object');
// Loop
for (const project of applicationProjects) {
// determine if valid e2e project exists
const e2eProject = Object.keys(projects.projects).map(key => projects.projects[key]).filter(x => { var _a; return (_a = x.implicitDependencies) === null || _a === void 0 ? void 0 : _a.includes(project.name); });
const firebaseJson = await (0, devkit_1.globAsync)(tree, [(0, devkit_1.joinPathFragments)(project.root, firebaseJsonGlob)]);
if (!e2eProject || e2eProject.length === 0) {
if ((!e2eProject || e2eProject.length === 0) && firebaseJson.length > 0) {
process.stdout.write(`The following project was found to not be covered by an e2e project and contain a firebase configuration: ${project.name}\r\nGenerating files for new E2E project ${project.name}-e2e\r\n`);
options.js = false;
}
// generate new files
await createE2EProject(project, tree, options);
}
else if (firebaseJson.length > 0) {
// update existing files
options = normalizeOptions(options, e2eProject[0], tree);
await injectConfiguration(tree, project, e2eProject[0], options);
}
}
if (cp !== undefined) {
await waitForChildProcess(cp);
}
// end loop
await (0, devkit_1.formatFiles)(tree);
return () => {
(0, devkit_1.installPackagesTask)(tree);
};
}
function waitForChildProcess(cp) {
return new Promise((res) => {
cp.on('exit', (code) => {
if (code === 1) {
throw new Error('Initialization of @nxextensions/nx-firebase failed');
}
res();
});
if (cp.exitCode !== undefined && cp.exitCode !== null) {
res();
}
});
}
async function createE2EProject(baseProject, tree, options) {
const appsDir = (0, devkit_1.getWorkspaceLayout)(tree).appsDir;
const newProjectName = `${baseProject.name}-e2e`;
const newProjectDir = (0, devkit_1.joinPathFragments)(appsDir, newProjectName);
const newProject = {
name: newProjectName,
projectType: 'application',
root: newProjectDir,
sourceRoot: (0, devkit_1.joinPathFragments)(newProjectDir, 'src'),
implicitDependencies: [baseProject.name]
};
options = normalizeOptions(options, newProject, tree);
(0, devkit_1.addProjectConfiguration)(tree, newProjectName, newProject);
await createCypressConfig(tree, newProject, options);
await (0, devkit_1.formatFiles)(tree);
await injectConfiguration(tree, baseProject, newProject, options);
}
async function createCypressConfig(tree, projectConfig, options) {
if (tree.exists((0, devkit_1.joinPathFragments)(projectConfig.root, 'cypress.config.ts')) ||
tree.exists((0, devkit_1.joinPathFragments)(projectConfig.root, 'cypress.config.js'))) {
return;
}
const templateVars = {
...options,
jsx: !!options.jsx,
offsetFromRoot: (0, devkit_1.offsetFromRoot)(projectConfig.root),
offsetFromProjectRoot: options.hasTsConfig ? options.offsetFromProjectRoot : '',
tsConfigPath: options.hasTsConfig ?
`${options.offsetFromProjectRoot}tsconfig.json` :
(0, js_1.getRelativePathToRootTsConfig)(tree, projectConfig.root),
ext: ''
};
(0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files/common'), projectConfig.root, templateVars);
if (options.js) {
if (isEsmProject(tree, projectConfig.root)) {
(0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files/config-js-esm'), projectConfig.root, templateVars);
}
else {
(0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files/config-js-cjs'), projectConfig.root, templateVars);
}
}
else {
(0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files/config-ts'), projectConfig.root, templateVars);
}
}
async function injectConfiguration(tree, projectConfig, e2eProject, options) {
var _a, _b, _c, _d;
const projectServeTarget = (_a = projectConfig.targets) === null || _a === void 0 ? void 0 : _a['serve'];
if (!projectServeTarget) {
console.warn(`The current project: ${projectConfig.name} does not have a serve target. Skipping configuration for this project`);
return;
}
const cyVersion = (0, cypress_version_1.installedCypressVersion)();
const filesToUse = cyVersion && cyVersion < 10 ? 'v9' : 'v10';
const hasTsConfig = tree.exists((0, devkit_1.joinPathFragments)(e2eProject.root, 'tsconfig.json'));
const offsetFromProjectRoot = options.directory
.split('/')
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.map(_ => '..')
.join('/');
const fileOpts = {
...options,
project: projectConfig.name,
dir: (_b = options.directory) !== null && _b !== void 0 ? _b : 'src',
ext: options.js ? 'js' : 'ts',
offsetFromRoot: (0, devkit_1.offsetFromRoot)(e2eProject.root),
offsetFromProjectRoot,
projectRoot: projectConfig.root,
tsConfigPath: hasTsConfig
? `${offsetFromProjectRoot}/tsconfig.json`
: (0, js_1.getRelativePathToRootTsConfig)(tree, e2eProject.root),
tmpl: ''
};
(0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files', filesToUse), e2eProject.root, fileOpts);
if (filesToUse === 'v10') {
// TODO: consider auto-detecting javascript
const cyFile = (0, devkit_1.joinPathFragments)(e2eProject.root, options.js ? 'cypress.config.js' : 'cypress.config.ts');
const webServerCommands = {};
let ciWebServerCommand;
const targetString = (0, devkit_1.targetToTargetString)({ project: projectConfig.name, target: 'serve' });
webServerCommands.default = `nx run ${targetString}`;
if ((_c = projectServeTarget.configurations) === null || _c === void 0 ? void 0 : _c['production']) {
webServerCommands.production = `nx run ${targetString}:production`;
}
if ((_d = projectConfig.targets) === null || _d === void 0 ? void 0 : _d['serve-static']) {
ciWebServerCommand = `nx run ${projectConfig.name}:serve-static`;
}
const updatedCyConfig = await (0, config_1.addDefaultE2eConfig)(tree.read(cyFile, 'utf-8'), {
cypressDir: options.directory,
bundler: options.bundler === 'vite' ? 'vite' : undefined,
webServerCommands,
ciWebServerCommand: ciWebServerCommand
}, options.baseUrl);
tree.write(cyFile, updatedCyConfig);
}
}
function addPlugin(tree, graph, updatePackageScripts) {
return (0, add_plugin_1.addPlugin)(tree, graph, '@nxextensions/firebase-cypress', target_generator_1.createNodesV2, {
targetName: ['e2e'],
openTargetName: ['open-cypress'],
componentTestingTargetName: ['component-test'],
ciTargetName: ['e2e-ci']
}, updatePackageScripts);
}
function isEsmProject(tree, projectRoot) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let packageJson;
if (tree.exists((0, devkit_1.joinPathFragments)(projectRoot, 'package.json'))) {
packageJson = (0, devkit_1.readJson)(tree, (0, devkit_1.joinPathFragments)(projectRoot, 'package.json'));
}
else {
packageJson = (0, devkit_1.readJson)(tree, 'package.json');
}
return packageJson.type === 'module';
}
// noinspection JSUnusedGlobalSymbols
exports.default = initGenerator;
//# sourceMappingURL=generator.js.map