@mschauer5/spfx-toolkit 1.0.1

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.
Files changed (65) hide show
  1. package/.vscode/settings.json +22 -0
  2. package/LICENSE +21 -0
  3. package/lib/commands/alias.command.js +104 -0
  4. package/lib/commands/alias.command.js.map +1 -0
  5. package/lib/commands/build.command.js +63 -0
  6. package/lib/commands/build.command.js.map +1 -0
  7. package/lib/commands/eslint.command.js +34 -0
  8. package/lib/commands/eslint.command.js.map +1 -0
  9. package/lib/commands/index.js +47 -0
  10. package/lib/commands/index.js.map +1 -0
  11. package/lib/commands/serve.command.js +27 -0
  12. package/lib/commands/serve.command.js.map +1 -0
  13. package/lib/commands/version.command.js +98 -0
  14. package/lib/commands/version.command.js.map +1 -0
  15. package/lib/common/constants.js +10 -0
  16. package/lib/common/constants.js.map +1 -0
  17. package/lib/common/index.js +43 -0
  18. package/lib/common/index.js.map +1 -0
  19. package/lib/common/logger.js +42 -0
  20. package/lib/common/logger.js.map +1 -0
  21. package/lib/common/util.js +80 -0
  22. package/lib/common/util.js.map +1 -0
  23. package/lib/index.js +132 -0
  24. package/lib/index.js.map +1 -0
  25. package/lib/package.json +44 -0
  26. package/lib/src/commands/alias.command.js +104 -0
  27. package/lib/src/commands/alias.command.js.map +1 -0
  28. package/lib/src/commands/build.command.js +61 -0
  29. package/lib/src/commands/build.command.js.map +1 -0
  30. package/lib/src/commands/bundle.command.js +70 -0
  31. package/lib/src/commands/bundle.command.js.map +1 -0
  32. package/lib/src/commands/eslint.command.js +34 -0
  33. package/lib/src/commands/eslint.command.js.map +1 -0
  34. package/lib/src/commands/index.js +49 -0
  35. package/lib/src/commands/index.js.map +1 -0
  36. package/lib/src/commands/serve.command.js +27 -0
  37. package/lib/src/commands/serve.command.js.map +1 -0
  38. package/lib/src/commands/version.command.js +98 -0
  39. package/lib/src/commands/version.command.js.map +1 -0
  40. package/lib/src/common/constants.js +10 -0
  41. package/lib/src/common/constants.js.map +1 -0
  42. package/lib/src/common/index.js +43 -0
  43. package/lib/src/common/index.js.map +1 -0
  44. package/lib/src/common/logger.js +42 -0
  45. package/lib/src/common/logger.js.map +1 -0
  46. package/lib/src/common/util.js +80 -0
  47. package/lib/src/common/util.js.map +1 -0
  48. package/lib/src/index.js +145 -0
  49. package/lib/src/index.js.map +1 -0
  50. package/lib/test/index.test.js +17 -0
  51. package/lib/test/index.test.js.map +1 -0
  52. package/package.json +44 -0
  53. package/src/commands/alias.command.ts +92 -0
  54. package/src/commands/build.command.ts +48 -0
  55. package/src/commands/bundle.command.ts +60 -0
  56. package/src/commands/eslint.command.ts +28 -0
  57. package/src/commands/index.ts +7 -0
  58. package/src/commands/serve.command.ts +13 -0
  59. package/src/commands/version.command.ts +104 -0
  60. package/src/common/constants.ts +4 -0
  61. package/src/common/index.ts +4 -0
  62. package/src/common/logger.ts +45 -0
  63. package/src/common/util.ts +55 -0
  64. package/src/index.ts +113 -0
  65. package/tsconfig.json +15 -0
@@ -0,0 +1,48 @@
1
+ import spawn from 'cross-spawn';
2
+ import { logger, util } from '../common';
3
+ import { detect } from 'detect-package-manager';
4
+
5
+ const packageRun = async (): Promise<void> => {
6
+ try {
7
+ const usercommand = await detect();
8
+
9
+ try {
10
+ const args = ['run', 'build'];
11
+ spawn.sync(usercommand, args, { stdio: 'inherit' });
12
+ } catch (error) {
13
+ logger.log(error);
14
+ }
15
+ } catch (error) {
16
+ logger.error(error);
17
+ }
18
+ };
19
+
20
+ export const run = async (usePackage: boolean, useDefault: boolean) => {
21
+ try {
22
+ let buildLocation = usePackage ? 'package' : await util.getScriptNameRunType('build');
23
+ if (buildLocation === undefined) {
24
+ buildLocation = 'default';
25
+ }
26
+
27
+ if (useDefault) {
28
+ buildLocation = usePackage ? 'package' : 'default';
29
+ await util.addScriptNameRunType('build', buildLocation);
30
+ }
31
+
32
+ if (buildLocation === 'package') {
33
+ await packageRun();
34
+ return;
35
+ }
36
+
37
+ const isGulp = await util.isUsingGulp();
38
+ let args = ['heft', 'build'];
39
+ if (isGulp) {
40
+ args = ['gulp', 'build'];
41
+ }
42
+
43
+ spawn.sync('npx', args, { stdio: 'inherit' });
44
+ return;
45
+ } catch (error) {
46
+ logger.error(error);
47
+ }
48
+ };
@@ -0,0 +1,60 @@
1
+ import spawn from 'cross-spawn';
2
+ import { logger, util } from '../common';
3
+ import { detect } from 'detect-package-manager';
4
+
5
+ const packageRun = async (): Promise<void> => {
6
+ try {
7
+ const usercommand = await detect();
8
+
9
+ try {
10
+ const args = ['run', 'bundle'];
11
+ spawn.sync(usercommand, args, { stdio: 'inherit' });
12
+ } catch (error) {
13
+ logger.log(error);
14
+ }
15
+ } catch (error) {
16
+ logger.error(error);
17
+ }
18
+ };
19
+
20
+ export const run = async (usePackage: boolean, useDefault: boolean) => {
21
+ try {
22
+ let bundleLocation = usePackage ? 'package' : await util.getScriptNameRunType('bundle');
23
+ if (bundleLocation === undefined) {
24
+ bundleLocation = 'default';
25
+ }
26
+
27
+ if (useDefault) {
28
+ bundleLocation = usePackage ? 'package' : 'default';
29
+ await util.addScriptNameRunType('bundle', bundleLocation);
30
+ }
31
+
32
+ if (bundleLocation === 'package') {
33
+ await packageRun();
34
+ return;
35
+ }
36
+
37
+ const isGulp = await util.isUsingGulp();
38
+ if (isGulp) {
39
+ let args = ['clean'];
40
+ spawn.sync('gulp', args, { stdio: 'inherit' });
41
+
42
+ args = ['bundle', '--ship'];
43
+ spawn.sync('gulp', args, { stdio: 'inherit' });
44
+
45
+ args = ['package-solution', '--ship'];
46
+ spawn.sync('gulp', args, { stdio: 'inherit' });
47
+ return;
48
+ }
49
+
50
+ // SPFx 1.22+ (Heft): build and package using Heft
51
+ let args = ['heft', 'build', '--clean'];
52
+ spawn.sync('npx', args, { stdio: 'inherit' });
53
+
54
+ args = ['heft', 'package'];
55
+ spawn.sync('npx', args, { stdio: 'inherit' });
56
+ return;
57
+ } catch (error) {
58
+ logger.error(error);
59
+ }
60
+ };
@@ -0,0 +1,28 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { constants, logger } from '../common';
4
+
5
+ const eslintFileName = '.eslintrc.js';
6
+ const eslint = path.resolve(constants.WORKING_DIRECTORY, eslintFileName);
7
+ const eslintBackupFileName = '.env-eslintrc-bk.js';
8
+ const eslintBackup = path.resolve(constants.WORKING_DIRECTORY, eslintBackupFileName);
9
+ export const backupEslint = () => {
10
+ if (!fs.existsSync(eslint)) {
11
+ logger.error(`No ${eslintFileName} found!`);
12
+ return;
13
+ }
14
+ fs.copyFileSync(eslint, eslintBackupFileName);
15
+ logger.info(`${eslintFileName} backed up to ${eslintBackupFileName}`);
16
+ };
17
+
18
+ export const restoreEslint = () => {
19
+ if (!fs.existsSync(eslintBackup)) {
20
+ logger.error(`No ${eslintBackupFileName} found!`);
21
+ return;
22
+ }
23
+ fs.copyFileSync(eslintBackup, eslintFileName);
24
+ logger.info(`${eslintFileName} restored from ${eslintBackupFileName}`);
25
+
26
+ // remove the backup file
27
+ fs.unlinkSync(eslintBackup);
28
+ };
@@ -0,0 +1,7 @@
1
+ import * as alias from './alias.command';
2
+ import * as eslint from './eslint.command';
3
+ import * as version from './version.command';
4
+ import * as serve from './serve.command';
5
+ import * as build from './build.command';
6
+ import * as bundle from './bundle.command';
7
+ export { alias, eslint, version, serve, build, bundle };
@@ -0,0 +1,13 @@
1
+ import { logger, util } from '../common';
2
+
3
+ export const run = async (): Promise<void> => {
4
+ logger.info('Starting the SPFx development server...');
5
+ util.isUsingGulp().then((usingGulp) => {
6
+ if (usingGulp) {
7
+ logger.info('Gulp detected. Running gulp serve...');
8
+ } else {
9
+ logger.error('Gulp not detected. Please ensure you have gulp installed and configured.');
10
+ }
11
+ });
12
+ // Implementation of the serve command goes here
13
+ };
@@ -0,0 +1,104 @@
1
+ import path from 'path';
2
+ import { constants, logger } from '../common';
3
+ import fs from 'fs';
4
+
5
+ const syncPackageSolution = (version: string) => {
6
+ const packageSolutionPath = path.resolve(constants.WORKING_DIRECTORY, 'config/package-solution.json');
7
+ const packageSolution = JSON.parse(fs.readFileSync(packageSolutionPath, 'utf-8'));
8
+ packageSolution.solution.version = version + '.0';
9
+ fs.writeFileSync(packageSolutionPath, JSON.stringify(packageSolution, null, 2));
10
+ logger.success(`updated sharepoint version to ${packageSolution.solution.version}`);
11
+ };
12
+
13
+ const updatePackage = (version: string) => {
14
+ const fileFullPath = path.resolve(constants.WORKING_DIRECTORY, 'package.json');
15
+ const json = JSON.parse(fs.readFileSync(fileFullPath, 'utf-8'));
16
+ json.version = version;
17
+ fs.writeFileSync(fileFullPath, JSON.stringify(json, null, 2));
18
+ logger.success(`updated package.json version to ${version}`);
19
+ };
20
+
21
+ const getPackageVersion = () => {
22
+ const fileFullPath = path.resolve(constants.WORKING_DIRECTORY, 'package.json');
23
+ if (!fs.existsSync(fileFullPath)) {
24
+ logger.error('package.json not found');
25
+ return;
26
+ }
27
+ const packageJson = JSON.parse(fs.readFileSync(fileFullPath, 'utf-8'));
28
+ return packageJson.version;
29
+ };
30
+
31
+ const getPackageSolutionVersion = (ignoreWarning: boolean = false) => {
32
+ const packageSolutionPath = path.resolve(constants.WORKING_DIRECTORY, 'config/package-solution.json');
33
+ if (!fs.existsSync(packageSolutionPath)) {
34
+ if (!ignoreWarning) logger.error('package-solution.json not found');
35
+ return;
36
+ }
37
+ const packageSolution = JSON.parse(fs.readFileSync(packageSolutionPath, 'utf-8'));
38
+ return packageSolution.solution.version;
39
+ };
40
+
41
+ export const listVersions = () => {
42
+ const jsonPackageV = getPackageVersion();
43
+ const sharepointVersion = getPackageSolutionVersion();
44
+
45
+ if (jsonPackageV === undefined || sharepointVersion === undefined) {
46
+ return;
47
+ }
48
+
49
+ logger.info(`package: ${jsonPackageV}`);
50
+ logger.info(`sharepoint: ${sharepointVersion}`);
51
+ console.log();
52
+ };
53
+
54
+ export const syncVersion = () => {
55
+ const fileFullPath = path.resolve(constants.WORKING_DIRECTORY, 'package.json');
56
+ const packageJson = JSON.parse(fs.readFileSync(fileFullPath, 'utf-8'));
57
+ syncPackageSolution(packageJson.version);
58
+ };
59
+
60
+ const incPackageVersion = (version: string, part: 'major' | 'minor' | 'patch' = 'patch'): string => {
61
+ // Split the version string into parts
62
+ const versionParts = version.split('.').map(Number);
63
+
64
+ if (versionParts.length !== 3 || versionParts.some(isNaN)) {
65
+ throw new Error('Invalid version format. Expected format: "x.y.z"');
66
+ }
67
+
68
+ // Increment the specified part
69
+ switch (part) {
70
+ case 'major':
71
+ versionParts[0] += 1;
72
+ versionParts[1] = 0; // Reset minor and patch to 0
73
+ versionParts[2] = 0;
74
+ break;
75
+ case 'minor':
76
+ versionParts[1] += 1;
77
+ versionParts[2] = 0; // Reset patch to 0
78
+ break;
79
+ case 'patch':
80
+ versionParts[2] += 1;
81
+ break;
82
+ }
83
+
84
+ // Join the parts back into a string
85
+ return versionParts.join('.');
86
+ };
87
+
88
+ export const incrementVersion = (part: 'major' | 'minor' | 'patch') => {
89
+ const jsonPackageV = getPackageVersion();
90
+ const sharepointVersion = getPackageSolutionVersion(true);
91
+
92
+ if (jsonPackageV === undefined) {
93
+ return;
94
+ }
95
+
96
+ const pV = incPackageVersion(jsonPackageV, part);
97
+
98
+ updatePackage(pV);
99
+ if (sharepointVersion === undefined) {
100
+ return;
101
+ }
102
+
103
+ syncPackageSolution(pV);
104
+ };
@@ -0,0 +1,4 @@
1
+ import path from 'path';
2
+
3
+ export const PACKAGE_DIRECTORY = path.resolve(__dirname, '..');
4
+ export const WORKING_DIRECTORY = process.cwd();
@@ -0,0 +1,4 @@
1
+ import { logger } from './logger';
2
+ import * as constants from './constants';
3
+ import * as util from './util';
4
+ export { logger, constants, util };
@@ -0,0 +1,45 @@
1
+ /* eslint-disable no-console */
2
+ import chalk from 'chalk';
3
+ import logSymbols from 'log-symbols';
4
+
5
+ class Logger {
6
+ private logger: (...args: any[]) => void;
7
+
8
+ constructor() {
9
+ const args = process.argv.slice(2);
10
+ const silent = args.indexOf('--silent') !== -1;
11
+
12
+ if (silent) {
13
+ this.logger = () => 0;
14
+ } else {
15
+ this.logger = console.log;
16
+ }
17
+ }
18
+
19
+ public success(message: string) {
20
+ this.logger(logSymbols.success, chalk.green(message));
21
+ this.newLine();
22
+ }
23
+
24
+ public log(message: string) {
25
+ this.logger(message);
26
+ }
27
+
28
+ public warning(message: string) {
29
+ this.logger(logSymbols.warning, chalk.yellowBright(message));
30
+ }
31
+
32
+ public error(message: string) {
33
+ this.logger(logSymbols.error, chalk.redBright(message));
34
+ }
35
+
36
+ public info(message: string) {
37
+ this.logger(logSymbols.info, chalk.blueBright(message));
38
+ }
39
+
40
+ public newLine() {
41
+ this.logger('');
42
+ }
43
+ }
44
+
45
+ export const logger = new Logger();
@@ -0,0 +1,55 @@
1
+ import { promises as fsPromises } from 'fs';
2
+ import os from 'os';
3
+ import path from 'path';
4
+
5
+ function getConfigPaths() {
6
+ const configDir = path.join(os.homedir(), '.spfx-toolkit');
7
+ const configPath = path.join(configDir, 'config.json');
8
+ return { configDir, configPath };
9
+ }
10
+
11
+ export async function checkIfFileExistsAsync(filename, includeError = true) {
12
+ try {
13
+ await fsPromises.readFile(filename, 'utf-8');
14
+ return true;
15
+ } catch (err) {
16
+ if (includeError) {
17
+ console.log(err);
18
+ }
19
+ return null;
20
+ }
21
+ }
22
+
23
+ export async function isUsingGulp(): Promise<boolean> {
24
+ const gulpFile = 'gulpfile.js';
25
+ return await checkIfFileExistsAsync(gulpFile, false);
26
+ }
27
+
28
+ export async function getScriptNameRunType(scriptName: string): Promise<any> {
29
+ const { configPath } = getConfigPaths();
30
+ try {
31
+ const data = await fsPromises.readFile(configPath, 'utf-8');
32
+ const config = JSON.parse(data);
33
+ if (config && typeof config === 'object' && scriptName in config) {
34
+ return config[scriptName];
35
+ }
36
+ return undefined;
37
+ } catch (err) {
38
+ // File does not exist or cannot be read
39
+ return undefined;
40
+ }
41
+ }
42
+
43
+ export async function addScriptNameRunType(scriptName: string, value: any): Promise<void> {
44
+ const { configDir, configPath } = getConfigPaths();
45
+ let config = {};
46
+ try {
47
+ const data = await fsPromises.readFile(configPath, 'utf-8');
48
+ config = JSON.parse(data);
49
+ } catch (err) {
50
+ // File does not exist or is invalid, start with empty config
51
+ }
52
+ config[scriptName] = value;
53
+ await fsPromises.mkdir(configDir, { recursive: true });
54
+ await fsPromises.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
55
+ }
package/src/index.ts ADDED
@@ -0,0 +1,113 @@
1
+ import chalk from 'chalk';
2
+ import pkg from '../package.json';
3
+ import * as commander from 'commander';
4
+ import { Command } from 'commander';
5
+ import * as commands from './commands';
6
+ import { logger } from './common';
7
+ const program = new Command();
8
+
9
+ const defaultToolkitName = 'spfx-toolkit';
10
+
11
+ program
12
+ .name('Matt Schauer SPFx Toolkit')
13
+ .description('CLI to help with SPFx development')
14
+ .addHelpText('beforeAll', chalk.blueBright('Developed by Matt Schauer'))
15
+ .version(pkg.version);
16
+
17
+ program
18
+ .command('add-alias')
19
+ .description('add alias')
20
+ .addArgument(new commander.Argument('<name>', 'alias name to use for this program'))
21
+ .action((name) => {
22
+ commands.alias.addAlias(name, defaultToolkitName);
23
+ });
24
+
25
+ program
26
+ .command('clear-alias')
27
+ .description('remove alias for spfx-toolkit')
28
+ .action(() => {
29
+ commands.alias.clearAlias(defaultToolkitName);
30
+ });
31
+
32
+ program
33
+ .command('serve')
34
+ .description('Serve / Start the SPFx Project')
35
+ .action(() => {
36
+ commands.serve.run();
37
+ });
38
+
39
+ program
40
+ .command('build')
41
+ .description('Build Project')
42
+ .option('-p, --usepackage', "Use package.json script 'build' as the command")
43
+ .option('-d, --isdefault', 'Set current command as default build action')
44
+ .action((options) => {
45
+ let usePackage = false;
46
+ let isdefault = false;
47
+ if (options.isdefault) {
48
+ isdefault = true;
49
+ }
50
+ if (options.usepackage) {
51
+ usePackage = true;
52
+ }
53
+
54
+ commands.build.run(usePackage, isdefault);
55
+ });
56
+
57
+ program
58
+ .command('bundle')
59
+ .description('Bundle Project')
60
+ .option('-p, --usepackage', "Use package.json script 'bundle' as the command")
61
+ .option('-d, --isdefault', 'Set current command as default bundle action')
62
+ .action((options) => {
63
+ let usePackage = false;
64
+ let isdefault = false;
65
+ if (options.isdefault) {
66
+ isdefault = true;
67
+ }
68
+ if (options.usepackage) {
69
+ usePackage = true;
70
+ }
71
+
72
+ commands.bundle.run(usePackage, isdefault);
73
+ });
74
+
75
+ program
76
+ .command('eslint')
77
+ .description('Backup and restore eslint file')
78
+ .option('-b, --backup', 'Backup the eslint file')
79
+ .option('-r, --restore', 'Restore from backup eslint file')
80
+ .action((options) => {
81
+ if (options.backup) {
82
+ commands.eslint.backupEslint();
83
+ } else if (options.restore) {
84
+ commands.eslint.restoreEslint();
85
+ } else {
86
+ logger.error('No action specified. Use --backup | -b <OR> --restore | -r');
87
+ }
88
+ });
89
+
90
+ program
91
+ .command('version')
92
+ .option('-l, --list', 'List SPFx project versions')
93
+ .option('-s, --sync', 'Sync package.json version with package-solution.json version')
94
+ .option('-i, --increment <part>', 'Increment package version (major, minor, patch)', (value: string) => {
95
+ if (!['major', 'minor', 'patch'].includes(value)) {
96
+ throw new Error(`Invalid increment part: ${value}. Must be one of major, minor, or patch.`);
97
+ }
98
+ return value; // Return the validated value
99
+ })
100
+ .action((options) => {
101
+ const { list, sync } = options;
102
+ if (list) {
103
+ commands.version.listVersions();
104
+ } else if (sync) {
105
+ commands.version.syncVersion();
106
+ } else if (options.increment) {
107
+ commands.version.incrementVersion(options.increment);
108
+ } else {
109
+ logger.error('No action specified. Use --list | -l OR --sync | -s OR --increment | -i');
110
+ }
111
+ });
112
+
113
+ program.parse();
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es6",
4
+ "module": "commonjs",
5
+ "esModuleInterop": true,
6
+ "resolveJsonModule": true,
7
+ "moduleResolution": "node",
8
+ "skipLibCheck": true,
9
+ "sourceMap": true,
10
+ "outDir": "lib",
11
+ "baseUrl": ".",
12
+ "lib": ["dom", "ESNext"]
13
+ },
14
+ "exclude": ["node_modules", "tests"]
15
+ }