@servicetitan/startup 32.3.2 → 32.5.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/dist/cli/commands/get-command.d.ts.map +1 -1
- package/dist/cli/commands/get-command.js +2 -0
- package/dist/cli/commands/get-command.js.map +1 -1
- package/dist/cli/commands/init.d.ts +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +6 -5
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/install.d.ts +1 -4
- package/dist/cli/commands/install.d.ts.map +1 -1
- package/dist/cli/commands/install.js +12 -118
- package/dist/cli/commands/install.js.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/lint.js +7 -55
- package/dist/cli/commands/lint.js.map +1 -1
- package/dist/cli/commands/mfe-list.d.ts.map +1 -1
- package/dist/cli/commands/mfe-list.js +2 -1
- package/dist/cli/commands/mfe-list.js.map +1 -1
- package/dist/cli/commands/review/rules/index.d.ts.map +1 -1
- package/dist/cli/commands/review/rules/index.js +2 -0
- package/dist/cli/commands/review/rules/index.js.map +1 -1
- package/dist/cli/commands/review/rules/no-deprecated-startup-install.d.ts +7 -0
- package/dist/cli/commands/review/rules/no-deprecated-startup-install.d.ts.map +1 -0
- package/dist/cli/commands/review/rules/no-deprecated-startup-install.js +38 -0
- package/dist/cli/commands/review/rules/no-deprecated-startup-install.js.map +1 -0
- package/dist/cli/commands/review/types.d.ts +1 -0
- package/dist/cli/commands/review/types.d.ts.map +1 -1
- package/dist/cli/commands/review/types.js.map +1 -1
- package/dist/cli/commands/stylelint.d.ts +17 -0
- package/dist/cli/commands/stylelint.d.ts.map +1 -0
- package/dist/cli/commands/stylelint.js +63 -0
- package/dist/cli/commands/stylelint.js.map +1 -0
- package/dist/cli/commands/upload-sourcemaps.d.ts.map +1 -1
- package/dist/cli/commands/upload-sourcemaps.js +2 -1
- package/dist/cli/commands/upload-sourcemaps.js.map +1 -1
- package/dist/cli/utils/cli-git.d.ts +0 -9
- package/dist/cli/utils/cli-git.d.ts.map +1 -1
- package/dist/cli/utils/cli-git.js +0 -59
- package/dist/cli/utils/cli-git.js.map +1 -1
- package/dist/cli/utils/cli-npm.d.ts +0 -3
- package/dist/cli/utils/cli-npm.d.ts.map +1 -1
- package/dist/cli/utils/cli-npm.js +0 -22
- package/dist/cli/utils/cli-npm.js.map +1 -1
- package/dist/cli/utils/index.d.ts +0 -1
- package/dist/cli/utils/index.d.ts.map +1 -1
- package/dist/cli/utils/index.js +0 -1
- package/dist/cli/utils/index.js.map +1 -1
- package/dist/cli/utils/lerna-exec.d.ts.map +1 -1
- package/dist/cli/utils/lerna-exec.js +2 -2
- package/dist/cli/utils/lerna-exec.js.map +1 -1
- package/dist/cli/utils/stylelint.d.ts +8 -0
- package/dist/cli/utils/stylelint.d.ts.map +1 -0
- package/dist/cli/utils/stylelint.js +67 -0
- package/dist/cli/utils/stylelint.js.map +1 -0
- package/dist/utils/get-configuration.d.ts +1 -0
- package/dist/utils/get-configuration.d.ts.map +1 -1
- package/dist/utils/get-configuration.js +1 -0
- package/dist/utils/get-configuration.js.map +1 -1
- package/package.json +8 -9
- package/src/cli/commands/__tests__/init.test.ts +11 -14
- package/src/cli/commands/__tests__/install.test.ts +19 -224
- package/src/cli/commands/__tests__/lint.test.ts +52 -10
- package/src/cli/commands/__tests__/mfe-list.test.ts +5 -4
- package/src/cli/commands/__tests__/stylelint.test.ts +32 -0
- package/src/cli/commands/__tests__/upload-sourcemaps.test.ts +4 -7
- package/src/cli/commands/get-command.ts +2 -0
- package/src/cli/commands/init.ts +6 -4
- package/src/cli/commands/install.ts +13 -116
- package/src/cli/commands/lint.ts +5 -57
- package/src/cli/commands/mfe-list.ts +2 -1
- package/src/cli/commands/review/rules/__tests__/no-deprecated-startup-install.test.ts +81 -0
- package/src/cli/commands/review/rules/index.ts +2 -0
- package/src/cli/commands/review/rules/no-deprecated-startup-install.ts +30 -0
- package/src/cli/commands/review/types.ts +1 -0
- package/src/cli/commands/stylelint.ts +26 -0
- package/src/cli/commands/upload-sourcemaps.ts +2 -1
- package/src/cli/utils/__tests__/cli-git.test.ts +4 -140
- package/src/cli/utils/__tests__/cli-npm.test.ts +0 -43
- package/src/cli/utils/__tests__/lerna-exec.test.ts +2 -2
- package/src/cli/utils/__tests__/stylelint.test.ts +164 -0
- package/src/cli/utils/cli-git.ts +1 -52
- package/src/cli/utils/cli-npm.ts +0 -12
- package/src/cli/utils/index.ts +1 -1
- package/src/cli/utils/lerna-exec.ts +1 -1
- package/src/cli/utils/stylelint.ts +55 -0
- package/src/utils/get-configuration.ts +1 -0
- package/dist/cli/utils/is-ci.d.ts +0 -2
- package/dist/cli/utils/is-ci.d.ts.map +0 -1
- package/dist/cli/utils/is-ci.js +0 -15
- package/dist/cli/utils/is-ci.js.map +0 -1
- package/src/cli/utils/__tests__/is-ci.test.ts +0 -40
- package/src/cli/utils/is-ci.ts +0 -3
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { stylelint } from '../../utils/stylelint';
|
|
2
|
+
import { Stylelint } from '../stylelint';
|
|
3
|
+
|
|
4
|
+
jest.mock('../../utils/stylelint', () => ({ stylelint: jest.fn() }));
|
|
5
|
+
|
|
6
|
+
describe(`[startup] ${Stylelint.name}`, () => {
|
|
7
|
+
let args: ConstructorParameters<typeof Stylelint>[0];
|
|
8
|
+
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
10
|
+
beforeEach(() => (args = { _: ['foo'], fix: false }));
|
|
11
|
+
|
|
12
|
+
const subject = async () => new Stylelint(args).execute();
|
|
13
|
+
|
|
14
|
+
test('runs stylelint with specified args', async () => {
|
|
15
|
+
await subject();
|
|
16
|
+
|
|
17
|
+
expect(stylelint).toHaveBeenCalledWith({ fix: args.fix, paths: args._ });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('with paths', () => {
|
|
21
|
+
beforeEach(() => (args.paths = ['bar']));
|
|
22
|
+
|
|
23
|
+
test('appends paths to args', async () => {
|
|
24
|
+
await subject();
|
|
25
|
+
|
|
26
|
+
expect(stylelint).toHaveBeenCalledWith({
|
|
27
|
+
fix: args.fix,
|
|
28
|
+
paths: [...args._, ...args.paths!],
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -1,17 +1,14 @@
|
|
|
1
|
+
import { isCI } from '@servicetitan/install';
|
|
1
2
|
import { execSync } from 'child_process';
|
|
2
|
-
import { vol
|
|
3
|
+
import { vol } from 'memfs';
|
|
3
4
|
import { inspect } from 'node:util';
|
|
4
5
|
import path from 'path';
|
|
5
|
-
import { isCI } from '../../utils';
|
|
6
6
|
import { log } from '../../../utils';
|
|
7
7
|
import { UploadSourcemaps } from '../upload-sourcemaps';
|
|
8
8
|
|
|
9
|
+
jest.mock('@servicetitan/install');
|
|
10
|
+
jest.mock('fs', () => require('memfs').fs);
|
|
9
11
|
jest.mock('child_process', () => ({ execSync: jest.fn() }));
|
|
10
|
-
jest.mock('fs', () => fs);
|
|
11
|
-
jest.mock('../../utils', () => ({
|
|
12
|
-
...jest.requireActual('../../utils'),
|
|
13
|
-
isCI: jest.fn(),
|
|
14
|
-
}));
|
|
15
12
|
jest.mock('../../../utils', () => ({
|
|
16
13
|
...jest.requireActual('../../../utils'),
|
|
17
14
|
log: { info: jest.fn(), warning: jest.fn() },
|
|
@@ -17,6 +17,7 @@ import { PreparePackage } from './prepare-package';
|
|
|
17
17
|
import { Review } from './review';
|
|
18
18
|
import { RunTask } from './run-task';
|
|
19
19
|
import { Start } from './start';
|
|
20
|
+
import { Stylelint } from './stylelint';
|
|
20
21
|
import { StylesCheck } from './styles-check';
|
|
21
22
|
import { Tests } from './test';
|
|
22
23
|
import { Command, Newable } from './types';
|
|
@@ -39,6 +40,7 @@ const commands: Record<CommandName, Newable<Command>> = {
|
|
|
39
40
|
[CommandName['prepare-package']]: PreparePackage,
|
|
40
41
|
[CommandName.review]: Review,
|
|
41
42
|
[CommandName.start]: Start,
|
|
43
|
+
[CommandName.stylelint]: Stylelint,
|
|
42
44
|
[CommandName['styles-check']]: StylesCheck,
|
|
43
45
|
[CommandName.test]: Tests,
|
|
44
46
|
[CommandName.task]: RunTask,
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { gitCloneRepo, gitIsReachable } from '@servicetitan/install';
|
|
1
2
|
import fs from 'fs';
|
|
2
3
|
import path from 'path';
|
|
3
4
|
|
|
4
5
|
import { log, logErrors } from '../../utils';
|
|
5
|
-
import { gitCloneRepo, gitIsReachable } from '../utils';
|
|
6
6
|
import { Command, CommandArgs } from './types';
|
|
7
7
|
|
|
8
8
|
interface Args extends CommandArgs {
|
|
@@ -28,7 +28,7 @@ export class Init extends Command<Args> {
|
|
|
28
28
|
throw new Error(`${destination} is not an empty directory`);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
if (
|
|
31
|
+
if (this.cloneRepo(destination)) {
|
|
32
32
|
log.info(`copied example project to ${destination}`);
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
@@ -36,10 +36,12 @@ export class Init extends Command<Args> {
|
|
|
36
36
|
if (!gitIsReachable({ name: REPO_NAME })) {
|
|
37
37
|
throw new Error(`could not read servicetitan/${REPO_NAME} repository`);
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
return Promise.resolve();
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
const ok =
|
|
43
|
+
cloneRepo(destination: string) {
|
|
44
|
+
const ok = gitCloneRepo({ destination, name: REPO_NAME });
|
|
43
45
|
if (!ok) {
|
|
44
46
|
return false;
|
|
45
47
|
}
|
|
@@ -1,142 +1,39 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
|
-
import
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import os from 'os';
|
|
5
|
-
import { log, logErrors, getStartupVersion, readJsonSafe } from '../../utils';
|
|
2
|
+
import { log, logErrors, getStartupVersion } from '../../utils';
|
|
6
3
|
import { Command, CommandArgs } from './types';
|
|
7
|
-
import { gitCloneRepo, isCI, npmWhoAmI } from '../utils';
|
|
8
|
-
|
|
9
4
|
interface Args extends CommandArgs {
|
|
10
5
|
fix?: boolean;
|
|
11
6
|
quiet?: boolean;
|
|
12
7
|
token?: boolean;
|
|
13
8
|
}
|
|
14
9
|
|
|
15
|
-
const REPO_NAME = 'frontend-dev-config';
|
|
16
|
-
const AUTH_TOKEN_KEY = '//registry.npmjs.org/:_authToken';
|
|
17
|
-
const AUTH_TOKEN_REGEX = /^\/\/registry\.npmjs\.org\/:_authToken=\s*\${([^}]+)}/m;
|
|
18
|
-
|
|
19
10
|
export class Install extends Command<Args> {
|
|
20
11
|
static readonly description = 'Install project dependencies';
|
|
21
12
|
static readonly options = {
|
|
22
13
|
fix: { boolean: true, describe: 'Update and dedupe package-lock.json', hidden: true },
|
|
23
|
-
|
|
14
|
+
quiet: { boolean: true, describe: 'Suppress output', hidden: true },
|
|
24
15
|
token: { boolean: true, describe: 'Configure npm token' },
|
|
25
16
|
};
|
|
26
17
|
|
|
27
18
|
@logErrors
|
|
28
19
|
async execute() {
|
|
29
|
-
if (!this.args
|
|
20
|
+
if (!this.args.quiet) {
|
|
30
21
|
log.info(`startup cli v${getStartupVersion()}`);
|
|
31
22
|
}
|
|
32
23
|
|
|
33
|
-
const
|
|
24
|
+
const options = [
|
|
25
|
+
this.args.fix ? '--fix' : '',
|
|
26
|
+
this.args.quiet ? '--quiet' : '',
|
|
27
|
+
this.args.token === true ? '--token' : '',
|
|
28
|
+
this.args.token === false ? '--no-token' : '',
|
|
29
|
+
].filter(option => !!option);
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
this.installPackages(env);
|
|
37
|
-
}
|
|
31
|
+
const command = `npx @servicetitan/install ${options.join(' ')}`.trim();
|
|
38
32
|
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
private async configureNpmToken() {
|
|
43
|
-
if (isCI() || this.args?.fix || this.args?.token === false) {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (!this.args?.quiet) {
|
|
48
|
-
log.info('Configuring NPM token ...');
|
|
49
|
-
}
|
|
33
|
+
log.debug('install', command);
|
|
50
34
|
|
|
51
|
-
|
|
52
|
-
const npmUser = npmWhoAmI();
|
|
53
|
-
/* istanbul ignore next: debug only */
|
|
54
|
-
log.debug('install:npm-user', () => JSON.stringify({ npmUser }));
|
|
55
|
-
if (npmUser === 'st-team') {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const token = await this.fetchNpmToken();
|
|
61
|
-
if (!token) {
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
execSync(`npm config set "${AUTH_TOKEN_KEY}"="${token}"`);
|
|
66
|
-
|
|
67
|
-
if (!fs.existsSync('.npmrc')) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const npmrc = fs.readFileSync('.npmrc', 'utf-8');
|
|
72
|
-
const match = AUTH_TOKEN_REGEX.exec(npmrc);
|
|
73
|
-
if (match) {
|
|
74
|
-
return { [match[1]]: token };
|
|
75
|
-
}
|
|
35
|
+
execSync(command, { stdio: 'inherit' });
|
|
76
36
|
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
private async fetchNpmToken() {
|
|
81
|
-
const tempDirPath = fs.mkdtempSync(path.join(os.tmpdir(), 'st-install-'));
|
|
82
|
-
try {
|
|
83
|
-
if (!(await gitCloneRepo({ destination: tempDirPath, name: REPO_NAME }))) {
|
|
84
|
-
throw new Error(`could not clone servicetitan/${REPO_NAME}`);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const npmJson = readJsonSafe(path.join(tempDirPath, '.npm.json'));
|
|
88
|
-
|
|
89
|
-
/* istanbul ignore next: debug only */
|
|
90
|
-
log.debug('install:fetch-token', () => JSON.stringify(npmJson, null, 2));
|
|
91
|
-
|
|
92
|
-
if (!((npmJson && typeof npmJson === 'object') || Array.isArray(npmJson))) {
|
|
93
|
-
throw new Error('.npm.json is not an object');
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const { readOnlyToken: authToken } = npmJson;
|
|
97
|
-
if (!authToken) {
|
|
98
|
-
throw new Error('.npm.json does not contain auth token');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (typeof authToken !== 'string') {
|
|
102
|
-
throw new Error('.npm.json auth token is not a string');
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return Buffer.from(authToken, 'base64').toString('utf-8');
|
|
106
|
-
} catch (e) {
|
|
107
|
-
log.warning(String(e));
|
|
108
|
-
} finally {
|
|
109
|
-
try {
|
|
110
|
-
fs.rmSync(tempDirPath, { recursive: true, force: true });
|
|
111
|
-
} catch {
|
|
112
|
-
// ignore
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
private installPackages(env?: Record<string, string>) {
|
|
118
|
-
/* istanbul ignore next: debug only */
|
|
119
|
-
log.debug('install:install-packages', () => JSON.stringify({ env }));
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Note, if these are changed, update bootstrap.js to match
|
|
123
|
-
* @see {@link file://./../../../../../bootstrap.js}
|
|
124
|
-
*/
|
|
125
|
-
const npmArguments = [
|
|
126
|
-
isCI() ? 'ci' : 'i',
|
|
127
|
-
'--audit=false',
|
|
128
|
-
'--fund=false',
|
|
129
|
-
'--legacy-peer-deps',
|
|
130
|
-
...(this.args?.fix ? ['--package-lock-only', '--prefer-dedupe'] : []),
|
|
131
|
-
].join(' ');
|
|
132
|
-
|
|
133
|
-
if (!this.args?.quiet) {
|
|
134
|
-
log.info(`Running npm ${npmArguments} ...`);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
execSync(`npm ${npmArguments}`, {
|
|
138
|
-
...(env ? { env: { ...process.env, ...env } } : {}),
|
|
139
|
-
stdio: 'inherit',
|
|
140
|
-
});
|
|
37
|
+
return Promise.resolve(); // stops "async method has no 'await' expression" lint error
|
|
141
38
|
}
|
|
142
39
|
}
|
package/src/cli/commands/lint.ts
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/naming-convention */
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
import stylelint from 'stylelint';
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
getDestinationFolders,
|
|
8
|
-
getPackages,
|
|
9
|
-
getStylelintConfiguration,
|
|
10
|
-
log,
|
|
11
|
-
logErrors,
|
|
12
|
-
} from '../../utils';
|
|
2
|
+
import { getPackages, log, logErrors } from '../../utils';
|
|
13
3
|
import { Command, CommandArgs } from './types';
|
|
14
4
|
import { eslint, lernaExec } from '../utils';
|
|
5
|
+
import { stylelint } from '../utils/stylelint';
|
|
15
6
|
|
|
16
7
|
interface Args extends CommandArgs {
|
|
17
8
|
_: string[];
|
|
@@ -61,58 +52,19 @@ export class Lint extends Command<Args> {
|
|
|
61
52
|
|
|
62
53
|
@logErrors
|
|
63
54
|
private async stylelint() {
|
|
64
|
-
const { disabled, ...config } = getStylelintConfiguration();
|
|
65
|
-
if (disabled) {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const { fix } = this.args;
|
|
70
|
-
const { paths } = this;
|
|
71
|
-
|
|
72
55
|
log.info('Running stylelint...');
|
|
73
|
-
|
|
74
|
-
const glob = `**/*.{${allowedExtensions.join(',')}}`;
|
|
75
|
-
let files = paths.reduce((result, path) => {
|
|
76
|
-
const extension = path.split('.').pop();
|
|
77
|
-
if (extension) {
|
|
78
|
-
if (allowedExtensions.includes(extension.toLowerCase())) {
|
|
79
|
-
result.push(path);
|
|
80
|
-
}
|
|
81
|
-
} else {
|
|
82
|
-
result.push(pathUniJoin(path, glob));
|
|
83
|
-
}
|
|
84
|
-
return result;
|
|
85
|
-
}, new Array<string>());
|
|
86
|
-
|
|
87
|
-
if (!files.length) {
|
|
88
|
-
files = [glob];
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const { report, errored } = await stylelint.lint({
|
|
92
|
-
files,
|
|
93
|
-
ignorePattern: ['node_modules', ...getDestinationFolders()],
|
|
94
|
-
formatter: 'string',
|
|
95
|
-
fix,
|
|
96
|
-
quietDeprecationWarnings: true,
|
|
97
|
-
...config,
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
process.stdout.write(report);
|
|
101
|
-
|
|
102
|
-
if (errored) {
|
|
103
|
-
process.exitCode = 1;
|
|
104
|
-
}
|
|
56
|
+
await stylelint({ fix: this.args.fix, paths: this.paths });
|
|
105
57
|
}
|
|
106
58
|
|
|
107
59
|
@logErrors
|
|
108
60
|
private async checkStyles() {
|
|
109
61
|
const { ignore, scope } = this.args;
|
|
110
62
|
|
|
111
|
-
const packages = getPackages({ ignore, scope });
|
|
63
|
+
const packages = ignore || scope ? getPackages({ ignore, scope }) : undefined;
|
|
112
64
|
|
|
113
65
|
await lernaExec({
|
|
114
66
|
cmd: 'startup styles-check',
|
|
115
|
-
scope: packages
|
|
67
|
+
scope: packages?.map(({ name }) => name),
|
|
116
68
|
stream: true,
|
|
117
69
|
});
|
|
118
70
|
}
|
|
@@ -128,7 +80,3 @@ export class Lint extends Command<Args> {
|
|
|
128
80
|
return [..._, ...paths];
|
|
129
81
|
}
|
|
130
82
|
}
|
|
131
|
-
|
|
132
|
-
function pathUniJoin(...chunks: string[]) {
|
|
133
|
-
return path.join(...chunks).replace(/\\/g, '/');
|
|
134
|
-
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/naming-convention */
|
|
2
|
+
import { npmWhoAmI } from '@servicetitan/install';
|
|
2
3
|
import chalk from 'chalk';
|
|
3
4
|
import Table from 'cli-table3';
|
|
4
5
|
import readline from 'readline/promises';
|
|
@@ -11,7 +12,7 @@ import {
|
|
|
11
12
|
PackageType,
|
|
12
13
|
} from '../../utils';
|
|
13
14
|
import { Command, CommandArgs } from './types';
|
|
14
|
-
import { isTTY, NPMPackageInfo, npmView,
|
|
15
|
+
import { isTTY, NPMPackageInfo, npmView, runCommand } from '../utils';
|
|
15
16
|
|
|
16
17
|
interface Args extends CommandArgs {
|
|
17
18
|
_: string[];
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import { FixCategory, Package, PackageError, ReviewConfiguration } from '../../types';
|
|
3
|
+
import { mockProject } from '../__mocks__';
|
|
4
|
+
|
|
5
|
+
import { NoDeprecatedStartupInstall } from '../no-deprecated-startup-install';
|
|
6
|
+
|
|
7
|
+
jest.mock('child_process', () => ({ execSync: jest.fn() }));
|
|
8
|
+
|
|
9
|
+
describe(`[startup] Review ${NoDeprecatedStartupInstall.name}`, () => {
|
|
10
|
+
const id = 'no-deprecated-startup-install';
|
|
11
|
+
const rule = new NoDeprecatedStartupInstall();
|
|
12
|
+
let config: ReviewConfiguration;
|
|
13
|
+
let pkg: Package;
|
|
14
|
+
let packages: Package[];
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
config = {};
|
|
18
|
+
pkg = { name: 'project', location: '.' };
|
|
19
|
+
packages = [pkg];
|
|
20
|
+
jest.clearAllMocks();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const subject = () => rule.run(mockProject({ config, packages }));
|
|
24
|
+
|
|
25
|
+
const fixSubject = () => rule.fix(subject()!);
|
|
26
|
+
|
|
27
|
+
function itReturnsNothing() {
|
|
28
|
+
test('returns nothing', () => {
|
|
29
|
+
expect(subject()).toBeUndefined();
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function itReturnsError() {
|
|
34
|
+
test('returns error', () => {
|
|
35
|
+
expect(subject()).toEqual({
|
|
36
|
+
id,
|
|
37
|
+
message: 'project uses deprecated "@servicetitan/startup install" script',
|
|
38
|
+
location: pkg.location,
|
|
39
|
+
fixable: FixCategory.isolated,
|
|
40
|
+
} satisfies PackageError);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
itReturnsNothing();
|
|
45
|
+
|
|
46
|
+
describe('when project uses deprecated bootstrap script', () => {
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
pkg.scripts = { bootstrap: 'npx --yes @servicetitan/startup install' };
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
itReturnsError();
|
|
52
|
+
|
|
53
|
+
test('fixes error', () => {
|
|
54
|
+
fixSubject();
|
|
55
|
+
|
|
56
|
+
expect(execSync).toHaveBeenCalledWith(
|
|
57
|
+
'npm pkg set scripts.bootstrap="npx --yes @servicetitan/install"'
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('when script includes version tag', () => {
|
|
62
|
+
beforeEach(() => {
|
|
63
|
+
pkg.scripts!.bootstrap = 'npx --yes @servicetitan/startup@30.0.0 install';
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
itReturnsError();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('with no root package', () => {
|
|
71
|
+
beforeEach(() => (packages = []));
|
|
72
|
+
|
|
73
|
+
itReturnsNothing();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('ignores invalid error', () => {
|
|
77
|
+
rule.fix({} as any);
|
|
78
|
+
|
|
79
|
+
expect(execSync).not.toHaveBeenCalled();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PackageRule } from '../types';
|
|
2
2
|
import { NoDeprecatedContentBase } from './no-deprecated-content-base';
|
|
3
|
+
import { NoDeprecatedStartupInstall } from './no-deprecated-startup-install';
|
|
3
4
|
import { NoDirectPeerDependencies } from './no-direct-peer-dependencies';
|
|
4
5
|
import { NoTypescriptEntryPoint } from './no-typescript-entry-point';
|
|
5
6
|
import { PreferOpenEndedPeerDependencies } from './prefer-open-ended-peer-dependencies';
|
|
@@ -24,6 +25,7 @@ export const rules: PackageRule[] = [
|
|
|
24
25
|
new RequireCompatibleLaunchDarklySdk(),
|
|
25
26
|
new NoDeprecatedContentBase(),
|
|
26
27
|
new NoDirectPeerDependencies(),
|
|
28
|
+
new NoDeprecatedStartupInstall(),
|
|
27
29
|
new NoTypescriptEntryPoint(),
|
|
28
30
|
new PreferOpenEndedPeerDependencies(),
|
|
29
31
|
new RequireServiceTitanScope(),
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import { FixCategory, PackageError, PackageRule, Project } from '../types';
|
|
3
|
+
|
|
4
|
+
export class NoDeprecatedStartupInstall implements PackageRule {
|
|
5
|
+
get id() {
|
|
6
|
+
return 'no-deprecated-startup-install';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
run(project: Project): PackageError | undefined {
|
|
10
|
+
const root = project.packages.find(({ location }) => location === '.');
|
|
11
|
+
const bootstrap = root?.scripts?.bootstrap;
|
|
12
|
+
|
|
13
|
+
if (bootstrap && /@servicetitan\/startup.* install/.test(bootstrap)) {
|
|
14
|
+
return {
|
|
15
|
+
id: this.id,
|
|
16
|
+
message: 'project uses deprecated "@servicetitan/startup install" script',
|
|
17
|
+
location: root.location,
|
|
18
|
+
fixable: FixCategory.isolated,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
fix({ location }: PackageError) {
|
|
24
|
+
if (!location) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
execSync('npm pkg set scripts.bootstrap="npx --yes @servicetitan/install"');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
2
|
+
import { logErrors } from '../../utils';
|
|
3
|
+
import { stylelint } from '../utils/stylelint';
|
|
4
|
+
import { Command, CommandArgs } from './types';
|
|
5
|
+
|
|
6
|
+
interface Args extends CommandArgs {
|
|
7
|
+
_: string[];
|
|
8
|
+
fix?: boolean;
|
|
9
|
+
paths?: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class Stylelint extends Command<Args> {
|
|
13
|
+
static readonly options = {
|
|
14
|
+
_: { description: '[paths...]' },
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
@logErrors
|
|
18
|
+
async execute() {
|
|
19
|
+
await stylelint({ fix: this.args.fix, paths: this.paths });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private get paths() {
|
|
23
|
+
const { _, paths = [] } = this.args;
|
|
24
|
+
return [..._, ...paths];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { isCI } from '@servicetitan/install';
|
|
1
2
|
import path from 'path';
|
|
2
3
|
import { execSync } from 'child_process';
|
|
3
4
|
import { getTsConfig, isWebComponent, log, logErrors, readJson } from '../../utils';
|
|
4
|
-
import {
|
|
5
|
+
import { TSConfig } from '../utils';
|
|
5
6
|
import { Command, CommandArgs } from './types';
|
|
6
7
|
|
|
7
8
|
interface Args extends CommandArgs {
|