@elliemae/ds-monorepo-devops 3.50.1-next.9 → 3.51.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.mjs +20 -0
- package/bin/ds-monorepo-devops.mjs +4 -0
- package/bin/execSyncNxTags/index.mjs +104 -0
- package/bin/execute-commands-map.mjs +33 -0
- package/configs/jest.config.mjs +67 -11
- package/configs/noConsoleMode.mjs +1 -0
- package/package.json +12 -2
package/bin/cli.mjs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import arg from 'arg';
|
|
2
|
+
import { checkCommandOrInquire, executeCommandsMap } from './execute-commands-map.mjs';
|
|
3
|
+
|
|
4
|
+
function parseArgumentsIntoOptions(rawArgs) {
|
|
5
|
+
const args = arg(
|
|
6
|
+
{},
|
|
7
|
+
{
|
|
8
|
+
argv: rawArgs.slice(2),
|
|
9
|
+
},
|
|
10
|
+
);
|
|
11
|
+
return {
|
|
12
|
+
command: args._[0],
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function cli(args) {
|
|
17
|
+
let options = parseArgumentsIntoOptions(args);
|
|
18
|
+
options = await checkCommandOrInquire(options);
|
|
19
|
+
executeCommandsMap(args, options);
|
|
20
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/* eslint-disable complexity, no-console, max-statements */
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import defGlob from 'glob';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
const { glob } = defGlob;
|
|
7
|
+
|
|
8
|
+
export async function promptForCommandOptions(options) {
|
|
9
|
+
const questions = [];
|
|
10
|
+
if (!options.hello) {
|
|
11
|
+
questions.push({
|
|
12
|
+
type: 'list',
|
|
13
|
+
name: 'dryRun',
|
|
14
|
+
message: 'dry-run?',
|
|
15
|
+
default: 'n',
|
|
16
|
+
choices: ['y', 'n'],
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
const answers = await inquirer.prompt(questions);
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
...answers,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* check all subfolders under cwd + packages/ for a package.json file
|
|
28
|
+
* - if it exists, check for project.json file in the same folder
|
|
29
|
+
* - if it doesn't exist, continue to the next folder
|
|
30
|
+
* - if both exist, check the "tags" array in the project.json file to
|
|
31
|
+
* - package.json `typesafety`
|
|
32
|
+
* - true
|
|
33
|
+
* - tag ensure "strict-eslint" exists in the tags array
|
|
34
|
+
* - false
|
|
35
|
+
* - tag remove "strict-eslint" from the tags array if it exists
|
|
36
|
+
* - `"status:eol"`
|
|
37
|
+
* - yes
|
|
38
|
+
* - remove the taxonomy tag from the tags array
|
|
39
|
+
* - no
|
|
40
|
+
* - add the taxonomy tag to the tags array if it doesn't exist or prompt the user to add it(in dry-run mode)
|
|
41
|
+
* (E.G. packages/layout/... -> "layout", packages/atom/... -> "atom")
|
|
42
|
+
* - if it doesn't exist, continue to the next folder
|
|
43
|
+
* @param options
|
|
44
|
+
* @param {string} [options.dryRun='n'] - Indicates whether to perform a dry run ('y' or 'n').
|
|
45
|
+
* @returns {Promise<void>}
|
|
46
|
+
*/
|
|
47
|
+
const fixMissingTags = async (options) => {
|
|
48
|
+
const { dryRun } = options;
|
|
49
|
+
const globPatternPackage = 'packages/**/*/package.json';
|
|
50
|
+
const packageJsonFilesPaths = glob(globPatternPackage, { sync: true, ignore: ['**/node_modules/**', '**/dist/**'] });
|
|
51
|
+
for (let i = 0; i < packageJsonFilesPaths.length; i += 1) {
|
|
52
|
+
const packageJsonFilePath = packageJsonFilesPaths[i];
|
|
53
|
+
const finalPath = path.resolve(process.cwd(), packageJsonFilePath);
|
|
54
|
+
const packageJson = JSON.parse(fs.readFileSync(finalPath, 'utf8'));
|
|
55
|
+
const { publishConfig: { typeSafety = false } = {} } = packageJson;
|
|
56
|
+
const taxonomy = finalPath.split('/').reverse()[2];
|
|
57
|
+
|
|
58
|
+
const projectJsonFilePath = path.resolve(finalPath, '../project.json');
|
|
59
|
+
if (!fs.existsSync(projectJsonFilePath)) continue;
|
|
60
|
+
const projectJson = JSON.parse(fs.readFileSync(projectJsonFilePath, 'utf8'));
|
|
61
|
+
const missingTags = [];
|
|
62
|
+
const extraTags = [];
|
|
63
|
+
|
|
64
|
+
const isMarkedAsStrict = projectJson.tags.includes('strict-eslint');
|
|
65
|
+
if (typeSafety && !isMarkedAsStrict) missingTags.push('strict-eslint');
|
|
66
|
+
if (!typeSafety && isMarkedAsStrict) extraTags.push('strict-eslint');
|
|
67
|
+
|
|
68
|
+
const isEol = projectJson.tags.includes('status:eol');
|
|
69
|
+
const hasTaxonomy = projectJson.tags.includes(taxonomy);
|
|
70
|
+
if (!isEol && !hasTaxonomy) missingTags.push(taxonomy);
|
|
71
|
+
if (isEol && hasTaxonomy) extraTags.push(taxonomy);
|
|
72
|
+
|
|
73
|
+
const shouldRemoveTags = extraTags.length !== 0;
|
|
74
|
+
const shouldAddTags = missingTags.length !== 0;
|
|
75
|
+
const shouldDoNothing = !shouldAddTags && !shouldRemoveTags;
|
|
76
|
+
if (shouldDoNothing) continue;
|
|
77
|
+
|
|
78
|
+
// mutate projectJson.tags
|
|
79
|
+
if (shouldAddTags) {
|
|
80
|
+
projectJson.tags.push(...missingTags);
|
|
81
|
+
}
|
|
82
|
+
// mutate projectJson.tags
|
|
83
|
+
if (shouldRemoveTags) {
|
|
84
|
+
projectJson.tags = projectJson.tags.filter((tag) => !extraTags.includes(tag));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (dryRun === 'n') {
|
|
88
|
+
projectJson.tags.sort();
|
|
89
|
+
fs.writeFileSync(projectJsonFilePath, JSON.stringify(projectJson, null, 2));
|
|
90
|
+
if (shouldAddTags) console.log(`Added ${missingTags.join(', ')} tag to ${projectJsonFilePath}`);
|
|
91
|
+
if (shouldRemoveTags) console.log(`Removed ${taxonomy} tag from ${projectJsonFilePath}`);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (dryRun === 'y') {
|
|
95
|
+
if (shouldAddTags) console.log(`Would have added ${missingTags.join(', ')} to ${projectJsonFilePath}`);
|
|
96
|
+
if (shouldRemoveTags) console.log(`Would have removed ${taxonomy} from ${projectJsonFilePath}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const execSyncNxTags = async (options) => {
|
|
102
|
+
const commandOptions = await promptForCommandOptions(options);
|
|
103
|
+
await fixMissingTags(commandOptions);
|
|
104
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import { execSyncNxTags } from './execSyncNxTags/index.mjs';
|
|
3
|
+
|
|
4
|
+
const COMMANDS = {
|
|
5
|
+
SYNC_NX_TAGS: 'sync-nx-tags',
|
|
6
|
+
EXIT: 'exit',
|
|
7
|
+
};
|
|
8
|
+
export const checkCommandOrInquire = async (options) => {
|
|
9
|
+
const questions = [];
|
|
10
|
+
if (!options.command) {
|
|
11
|
+
questions.push({
|
|
12
|
+
type: 'list',
|
|
13
|
+
name: 'command',
|
|
14
|
+
message: 'Please choose which command to run',
|
|
15
|
+
choices: Object.values(COMMANDS),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
const answers = await inquirer.prompt(questions);
|
|
19
|
+
return {
|
|
20
|
+
...options,
|
|
21
|
+
...answers,
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export async function executeCommandsMap(args, options) {
|
|
26
|
+
switch (options.command) {
|
|
27
|
+
case COMMANDS.SYNC_NX_TAGS:
|
|
28
|
+
execSyncNxTags(options);
|
|
29
|
+
break;
|
|
30
|
+
default:
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
}
|
package/configs/jest.config.mjs
CHANGED
|
@@ -1,14 +1,70 @@
|
|
|
1
1
|
import { jestConfig } from '@elliemae/pui-cli';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file
|
|
5
|
+
const __dirname = path.dirname(__filename); // get the name of the directory
|
|
6
|
+
const getFileFromCurrentFolder = (fileName) => path.normalize(path.resolve(__dirname, fileName));
|
|
7
|
+
/**
|
|
8
|
+
* Generates an array of setup files for Jest based on provided flags.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} options - The options object.
|
|
11
|
+
* @param {boolean} [options.silenceConsole=false] - Flag to determine if console output should be silenced.
|
|
12
|
+
* @returns {string[]} An array of setup files for Jest.
|
|
13
|
+
*/
|
|
14
|
+
const getSetupFilesAfterEnvBasedOnFlags = ({ silenceConsole = false }) => {
|
|
15
|
+
const setupFilesAfterEnv = [];
|
|
2
16
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
17
|
+
if (silenceConsole) {
|
|
18
|
+
setupFilesAfterEnv.push(getFileFromCurrentFolder('noConsoleMode.mjs'));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return setupFilesAfterEnv;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Generates a Jest configuration object.
|
|
26
|
+
*
|
|
27
|
+
* @param {Object} options - Configuration options.
|
|
28
|
+
* @param {string} [options.relativePathAfterTestsFolder] - Relative path after the tests folder to match test files.
|
|
29
|
+
* @param {Object} [options.firstLevelSpread] - Configuration options to override defaults.
|
|
30
|
+
* @param {Object} options.flags - Flags for configuration.
|
|
31
|
+
* @param {boolean} [options.flags.silenceConsole=false] - Flag to silence the console during tests.
|
|
32
|
+
* @returns {import('jest').Config} Jest configuration object.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* config({
|
|
36
|
+
* // matches tests/GlobalHeader.keyboard.test.js
|
|
37
|
+
* relativePathAfterTestsFolder: 'GlobalHeader.keyboard.test.js',
|
|
38
|
+
* // overrides the default configuration for jest config 'verbose' to true
|
|
39
|
+
* firstLevelSpread: { verbose: true },
|
|
40
|
+
* // silences the console during tests via custom setup file
|
|
41
|
+
* flags: { silenceConsole: true }
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* // matches tests/GlobalHeader/a11y/test.keys.js
|
|
46
|
+
* config({
|
|
47
|
+
* relativePathAfterTestsFolder: 'GlobalHeader/a11y/test.keys.js',
|
|
48
|
+
* flags: { silenceConsole: true }
|
|
49
|
+
* });
|
|
50
|
+
*/
|
|
51
|
+
export const config = ({ relativePathAfterTestsFolder = undefined, firstLevelSpread = {}, flags = {} } = {}) => {
|
|
52
|
+
const silenceConsole = flags.silenceConsole ?? process.env.JEST_FORCE_SILENT_CONSOLE ?? false;
|
|
53
|
+
|
|
54
|
+
/** @type {import('jest').Config} */
|
|
55
|
+
return {
|
|
56
|
+
...jestConfig,
|
|
57
|
+
collectCoverage: false,
|
|
58
|
+
collectCoverageFrom: [],
|
|
59
|
+
coverageDirectory: undefined,
|
|
60
|
+
coverageReporters: undefined,
|
|
61
|
+
// testRegex: 'tests/.*\\.test\\.[jt]sx?$',
|
|
62
|
+
testRegex: relativePathAfterTestsFolder
|
|
63
|
+
? `.*/tests/${relativePathAfterTestsFolder}$`
|
|
64
|
+
: 'tests/.*\\.test\\.[jt]sx?$',
|
|
65
|
+
testTimeout: 120000,
|
|
66
|
+
setupFilesAfterEnv: [...jestConfig.setupFilesAfterEnv, ...getSetupFilesAfterEnvBasedOnFlags({ silenceConsole })],
|
|
67
|
+
...firstLevelSpread,
|
|
68
|
+
};
|
|
69
|
+
};
|
|
14
70
|
export default config;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
global.console = { jestLog: console.log, error: jest.fn(), warn: jest.fn(), log: jest.fn() };
|
package/package.json
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elliemae/ds-monorepo-devops",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.51.0-next.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "ICE MT - Dimsum - Monorepo Devops",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"files": [
|
|
8
|
+
"bin",
|
|
7
9
|
"configs"
|
|
8
10
|
],
|
|
11
|
+
"bin": {
|
|
12
|
+
"@elliemae/ds-monorepo-devops": "./bin/ds-monorepo-devops.mjs",
|
|
13
|
+
"ds-monorepo-devops": "./bin/ds-monorepo-devops.mjs"
|
|
14
|
+
},
|
|
9
15
|
"exports": {
|
|
10
16
|
"./configs/jest.config": {
|
|
11
17
|
"import": "./configs/jest.config.mjs",
|
|
@@ -29,7 +35,11 @@
|
|
|
29
35
|
"indent": 4
|
|
30
36
|
},
|
|
31
37
|
"peerDependencies": {
|
|
32
|
-
"@elliemae/pui-cli": "9.0.0-next.
|
|
38
|
+
"@elliemae/pui-cli": "9.0.0-next.50",
|
|
39
|
+
"arg": "~5.0.2",
|
|
40
|
+
"glob": "~10.2.5",
|
|
41
|
+
"ignore": "^5.3.0",
|
|
42
|
+
"inquirer": "~12.0.0",
|
|
33
43
|
"jest": "~29.7.0"
|
|
34
44
|
},
|
|
35
45
|
"publishConfig": {
|