@mschauer5/spfx-toolkit 1.0.10 → 1.0.11
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/README.md +13 -1
- package/lib/commands/build.command.js +41 -60
- package/lib/commands/build.command.js.map +1 -1
- package/lib/commands/bundle.command.js +50 -69
- package/lib/commands/bundle.command.js.map +1 -1
- package/lib/commands/index.js +5 -7
- package/lib/commands/index.js.map +1 -1
- package/lib/commands/open-solution.command.js +30 -0
- package/lib/commands/open-solution.command.js.map +1 -0
- package/lib/commands/scripts.command.js +153 -0
- package/lib/commands/scripts.command.js.map +1 -0
- package/lib/commands/settings.js +68 -0
- package/lib/commands/settings.js.map +1 -0
- package/lib/common/index.js.map +1 -1
- package/lib/common/util.js +84 -32
- package/lib/common/util.js.map +1 -1
- package/lib/index.js +55 -25
- package/lib/index.js.map +1 -1
- package/mschauer5-spfx-toolkit-1.0.10.tgz +0 -0
- package/package.json +2 -4
- package/src/commands/build.command.ts +41 -41
- package/src/commands/bundle.command.ts +60 -60
- package/src/commands/index.ts +3 -4
- package/src/commands/open-solution.command.ts +25 -0
- package/src/commands/scripts.command.ts +143 -0
- package/src/common/index.ts +1 -0
- package/src/common/util.ts +83 -27
- package/src/index.ts +61 -27
- package/src/commands/serve.command.ts +0 -13
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { logger, util } from '../common';
|
|
2
|
+
import spawn from 'cross-spawn';
|
|
3
|
+
import { promises as fsPromises } from 'fs';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { incrementVersion, syncVersion } from './version.command';
|
|
7
|
+
|
|
8
|
+
function getProjectConfigPaths() {
|
|
9
|
+
const configDir = path.join(process.cwd());
|
|
10
|
+
const configPath = path.join(configDir, 'package.json');
|
|
11
|
+
return { configDir, configPath };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function getScriptValue(scriptName: string, configPath: string): Promise<any> {
|
|
15
|
+
try {
|
|
16
|
+
const fileExists = await util.checkIfFileExistsAsync(configPath, false);
|
|
17
|
+
if (!fileExists) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const isGulp = await util.isUsingGulp();
|
|
22
|
+
let suffix = ':heft';
|
|
23
|
+
if (isGulp) {
|
|
24
|
+
suffix = ':gulp';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const data = await fsPromises.readFile(configPath, 'utf-8');
|
|
28
|
+
const config = JSON.parse(data);
|
|
29
|
+
if (config && typeof config === 'object' && config.scripts) {
|
|
30
|
+
const scriptWithSuffix = scriptName + suffix;
|
|
31
|
+
const hasBase = scriptName in config.scripts;
|
|
32
|
+
const hasSuffix = scriptWithSuffix in config.scripts;
|
|
33
|
+
if (hasBase && hasSuffix) {
|
|
34
|
+
return config.scripts[scriptWithSuffix];
|
|
35
|
+
} else if (hasSuffix) {
|
|
36
|
+
return config.scripts[scriptWithSuffix];
|
|
37
|
+
} else if (hasBase) {
|
|
38
|
+
return config.scripts[scriptName];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
} catch (err) {
|
|
43
|
+
// File does not exist or cannot be read
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function getScriptByName(scriptName: string): Promise<string | undefined> {
|
|
49
|
+
const globalPath = util.getGlobalConfigPaths();
|
|
50
|
+
const localPath = getProjectConfigPaths();
|
|
51
|
+
let value = undefined;
|
|
52
|
+
value = await getScriptValue(scriptName, globalPath.configPath);
|
|
53
|
+
|
|
54
|
+
const localValue = await getScriptValue(scriptName, localPath.configPath);
|
|
55
|
+
if (localValue !== undefined) {
|
|
56
|
+
value = localValue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const run = async (scriptName: string, options?: any) => {
|
|
63
|
+
if (scriptName === 'bundle' && options && typeof options === 'string') {
|
|
64
|
+
if (options) {
|
|
65
|
+
incrementVersion(options as 'major' | 'minor' | 'patch');
|
|
66
|
+
} else {
|
|
67
|
+
syncVersion();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (scriptName === 'serve') {
|
|
72
|
+
const isGulp = await util.isUsingGulp();
|
|
73
|
+
if (isGulp) {
|
|
74
|
+
const envFilePath = path.join(process.cwd(), '.env');
|
|
75
|
+
const envFileExists = await util.checkIfFileExistsAsync(envFilePath, false);
|
|
76
|
+
if (envFileExists) {
|
|
77
|
+
const envContent = await fsPromises.readFile(envFilePath, 'utf-8');
|
|
78
|
+
const lines = envContent.split(/\r?\n/);
|
|
79
|
+
const envVars: { [key: string]: string } = {};
|
|
80
|
+
for (const line of lines) {
|
|
81
|
+
const [key, value] = line.split('=');
|
|
82
|
+
if (key && value) {
|
|
83
|
+
envVars[key.trim()] = value.trim();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (envVars['tenantDomain']) {
|
|
87
|
+
process.env['SPFX_SERVE_TENANT_DOMAIN'] = envVars['tenantDomain'];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const value = await getScriptByName(scriptName);
|
|
94
|
+
if (!value) {
|
|
95
|
+
logger.error(`Script name '${scriptName}' not found in either local or global package.json!`);
|
|
96
|
+
} else {
|
|
97
|
+
try {
|
|
98
|
+
spawn.sync(value, { stdio: 'inherit', shell: true, cwd: process.cwd() });
|
|
99
|
+
} catch (error) {
|
|
100
|
+
logger.error(error);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const sync = async (scriptName: string, to: 'global' | 'local') => {
|
|
106
|
+
const { configPath: globalConfigPath } = util.getGlobalConfigPaths();
|
|
107
|
+
const { configPath: localConfigPath } = getProjectConfigPaths();
|
|
108
|
+
let sourcePath = to === 'local' ? globalConfigPath : localConfigPath;
|
|
109
|
+
|
|
110
|
+
const scriptValue = await getScriptValue(scriptName, sourcePath);
|
|
111
|
+
if (!scriptValue) {
|
|
112
|
+
logger.error(`Source script name '${scriptName}' not found in the ${sourcePath}!`);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const targetPath = to === 'local' ? localConfigPath : globalConfigPath;
|
|
117
|
+
const targetExists = await util.checkIfFileExistsAsync(targetPath, false);
|
|
118
|
+
if (!targetExists) {
|
|
119
|
+
logger.error(`Target package.json does not exist at path: ${targetPath}`);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const data = await fsPromises.readFile(targetPath, 'utf-8');
|
|
123
|
+
const targetConfig = JSON.parse(data);
|
|
124
|
+
if (!targetConfig.scripts) {
|
|
125
|
+
targetConfig.scripts = {};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const isGulp = await util.isUsingGulp();
|
|
129
|
+
let suffix = ':heft';
|
|
130
|
+
const orgScriptName = scriptName;
|
|
131
|
+
if (isGulp) {
|
|
132
|
+
suffix = ':gulp';
|
|
133
|
+
}
|
|
134
|
+
if (to === 'global') {
|
|
135
|
+
scriptName = scriptName + suffix;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
targetConfig.scripts[scriptName] = scriptValue;
|
|
139
|
+
|
|
140
|
+
await fsPromises.writeFile(targetPath, JSON.stringify(targetConfig, null, 2), 'utf-8');
|
|
141
|
+
|
|
142
|
+
logger.log(`Synchronized script '${orgScriptName}' to ${targetPath}`);
|
|
143
|
+
};
|
package/src/common/index.ts
CHANGED
package/src/common/util.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { promises as fsPromises } from 'fs';
|
|
2
2
|
import os from 'os';
|
|
3
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
|
-
}
|
|
4
|
+
import { logger } from './logger';
|
|
10
5
|
|
|
11
6
|
export async function checkIfFileExistsAsync(filename, includeError = true) {
|
|
12
7
|
try {
|
|
@@ -20,36 +15,97 @@ export async function checkIfFileExistsAsync(filename, includeError = true) {
|
|
|
20
15
|
}
|
|
21
16
|
}
|
|
22
17
|
|
|
18
|
+
export function getGlobalConfigPaths() {
|
|
19
|
+
const configDir = path.join(os.homedir(), '.spfx-toolkit');
|
|
20
|
+
const configPath = path.join(configDir, 'package.json');
|
|
21
|
+
return { configDir, configPath };
|
|
22
|
+
}
|
|
23
|
+
|
|
23
24
|
export async function isUsingGulp(): Promise<boolean> {
|
|
24
25
|
const gulpFile = 'gulpfile.js';
|
|
25
26
|
return await checkIfFileExistsAsync(gulpFile, false);
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
const { configPath } =
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
async function ensureGlobalPackageJsonExists() {
|
|
30
|
+
const { configDir, configPath } = getGlobalConfigPaths();
|
|
31
|
+
const exists = await checkIfFileExistsAsync(configPath, false);
|
|
32
|
+
if (!exists) {
|
|
33
|
+
await fsPromises.mkdir(configDir, { recursive: true });
|
|
34
|
+
await fsPromises.writeFile(configPath, JSON.stringify({ scripts: {} }, null, 2), 'utf-8');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function openGlobalPackageJsonInEditor() {
|
|
39
|
+
const { configPath } = getGlobalConfigPaths();
|
|
40
|
+
await ensureGlobalPackageJsonExists();
|
|
41
|
+
|
|
42
|
+
const spawn = require('child_process').spawn;
|
|
43
|
+
// Open the global package.json in VS Code
|
|
44
|
+
spawn(process.platform === 'win32' ? 'code.cmd' : 'code', [configPath], {
|
|
45
|
+
stdio: 'inherit',
|
|
46
|
+
shell: true
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function initGlobalPackageJsonWithDefaults(force?: boolean) {
|
|
51
|
+
const { configPath } = getGlobalConfigPaths();
|
|
52
|
+
ensureGlobalPackageJsonExists();
|
|
53
|
+
|
|
54
|
+
const defaultScripts = {
|
|
55
|
+
'build:gulp': 'gulp build',
|
|
56
|
+
'build:heft': 'heft build',
|
|
57
|
+
'bundle:gulp': 'gulp clean && gulp bundle --ship && gulp package-solution --ship',
|
|
58
|
+
'bundle:heft': 'heft build --clean && heft package',
|
|
59
|
+
'serve:heft': 'heft start --clean',
|
|
60
|
+
'serve:gulp': 'gulp serve'
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const packageJsonContent = await fsPromises.readFile(configPath, 'utf-8');
|
|
64
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
65
|
+
|
|
66
|
+
// Only add/overwrite scripts if force is true, otherwise do not overwrite existing matching scripts
|
|
67
|
+
packageJson.scripts = packageJson.scripts || {};
|
|
68
|
+
for (const [key, value] of Object.entries(defaultScripts)) {
|
|
69
|
+
if (force || !(key in packageJson.scripts)) {
|
|
70
|
+
packageJson.scripts[key] = value;
|
|
35
71
|
}
|
|
36
|
-
return undefined;
|
|
37
|
-
} catch (err) {
|
|
38
|
-
// File does not exist or cannot be read
|
|
39
|
-
return undefined;
|
|
40
72
|
}
|
|
73
|
+
|
|
74
|
+
await fsPromises.writeFile(configPath, JSON.stringify(packageJson, null, 2), 'utf-8');
|
|
41
75
|
}
|
|
42
76
|
|
|
43
|
-
export
|
|
44
|
-
|
|
45
|
-
|
|
77
|
+
export const installHeftDotenvPlugin = async () => {
|
|
78
|
+
if (await isUsingGulp()) {
|
|
79
|
+
logger.error('Heft .env plugin can only be installed in projects using Heft, but Gulp was detected.');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const spawn = require('child_process').spawnSync;
|
|
84
|
+
const result = spawn('npm', ['install', '--save-dev', '@mschauer5/heft-dotenv-plugin'], {
|
|
85
|
+
stdio: 'inherit',
|
|
86
|
+
shell: true
|
|
87
|
+
});
|
|
88
|
+
if (result.error) {
|
|
89
|
+
logger.error(`Error installing @mschauer5/heft-dotenv-plugin: ${result.error.message}`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Modify heft.json to include the dotenv plugin
|
|
94
|
+
const heftConfigPath = path.join(process.cwd(), 'heft.json');
|
|
95
|
+
let heftConfig: any = {};
|
|
46
96
|
try {
|
|
47
|
-
const
|
|
48
|
-
|
|
97
|
+
const heftConfigContent = await fsPromises.readFile(heftConfigPath, 'utf-8');
|
|
98
|
+
heftConfig = JSON.parse(heftConfigContent);
|
|
49
99
|
} catch (err) {
|
|
50
|
-
|
|
100
|
+
logger.error(`Error reading heft.json: ${err.message}`);
|
|
101
|
+
return;
|
|
51
102
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
103
|
+
heftConfig.heftPlugins = heftConfig.heftPlugins || [];
|
|
104
|
+
if (!heftConfig.heftPlugins.some((plugin: any) => plugin.pluginPackage === '@mschauer5/heft-dotenv-plugin')) {
|
|
105
|
+
heftConfig.heftPlugins.push({ pluginPackage: '@mschauer5/heft-dotenv-plugin' });
|
|
106
|
+
await fsPromises.writeFile(heftConfigPath, JSON.stringify(heftConfig, null, 2), 'utf-8');
|
|
107
|
+
logger.success('Added @mschauer5/heft-dotenv-plugin to heft.json plugins.');
|
|
108
|
+
} else {
|
|
109
|
+
logger.info('@mschauer5/heft-dotenv-plugin is already included in heft.json plugins.');
|
|
110
|
+
}
|
|
111
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ import chalk from 'chalk';
|
|
|
3
3
|
import * as commander from 'commander';
|
|
4
4
|
import { Command } from 'commander';
|
|
5
5
|
import * as commands from './commands';
|
|
6
|
-
import { logger } from './common';
|
|
6
|
+
import { logger, util } from './common';
|
|
7
7
|
const program = new Command();
|
|
8
8
|
|
|
9
9
|
const defaultToolkitName = 'spfx-toolkit';
|
|
@@ -12,7 +12,7 @@ program
|
|
|
12
12
|
.name('Matt Schauer SPFx Toolkit')
|
|
13
13
|
.description('CLI to help with SPFx development')
|
|
14
14
|
.addHelpText('beforeAll', chalk.blueBright('Developed by Matt Schauer'))
|
|
15
|
-
.version('1.0.
|
|
15
|
+
.version('1.0.11');
|
|
16
16
|
|
|
17
17
|
program
|
|
18
18
|
.command('add-alias')
|
|
@@ -24,7 +24,7 @@ program
|
|
|
24
24
|
|
|
25
25
|
program
|
|
26
26
|
.command('clear-alias')
|
|
27
|
-
.description('remove alias for spfx-toolkit')
|
|
27
|
+
.description('remove any alias for spfx-toolkit')
|
|
28
28
|
.action(() => {
|
|
29
29
|
commands.alias.clearAlias(defaultToolkitName);
|
|
30
30
|
});
|
|
@@ -33,43 +33,77 @@ program
|
|
|
33
33
|
.command('serve')
|
|
34
34
|
.description('Serve / Start the SPFx Project')
|
|
35
35
|
.action(() => {
|
|
36
|
-
commands.
|
|
36
|
+
commands.scripts.run('serve');
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
program
|
|
40
40
|
.command('build')
|
|
41
41
|
.description('Build Project')
|
|
42
|
-
.
|
|
43
|
-
|
|
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);
|
|
42
|
+
.action(() => {
|
|
43
|
+
commands.scripts.run('build');
|
|
55
44
|
});
|
|
56
45
|
|
|
57
46
|
program
|
|
58
47
|
.command('bundle')
|
|
59
48
|
.description('Bundle Project')
|
|
60
|
-
.option('-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
let usePackage = false;
|
|
64
|
-
let isdefault = false;
|
|
65
|
-
if (options.isdefault) {
|
|
66
|
-
isdefault = true;
|
|
49
|
+
.option('-i, --increment <part>', 'Increment package version (major, minor, patch)', (value: string) => {
|
|
50
|
+
if (!['major', 'minor', 'patch'].includes(value)) {
|
|
51
|
+
throw new Error(`Invalid increment part: ${value}. Must be one of major, minor, or patch.`);
|
|
67
52
|
}
|
|
68
|
-
|
|
69
|
-
|
|
53
|
+
return value; // Return the validated value
|
|
54
|
+
})
|
|
55
|
+
.action((options) => {
|
|
56
|
+
commands.scripts.run('bundle', options.increment);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
program
|
|
60
|
+
.command('run')
|
|
61
|
+
.description('Run a script from package.json by name')
|
|
62
|
+
.addArgument(new commander.Argument('<name>', 'script name to run from package.json'))
|
|
63
|
+
.action((name) => {
|
|
64
|
+
commands.scripts.run(name);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
program
|
|
68
|
+
.command('open-global-config')
|
|
69
|
+
.description('Open global package.json for spfx-toolkit')
|
|
70
|
+
.action(() => {
|
|
71
|
+
util.openGlobalPackageJsonInEditor();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
program
|
|
75
|
+
.command('global-config-init')
|
|
76
|
+
.description('Set default scripts in global package.json for spfx-toolkit')
|
|
77
|
+
.option('-f, --force', 'If set will overwrite existing scripts names in global package.json')
|
|
78
|
+
.action((options) => {
|
|
79
|
+
util.initGlobalPackageJsonWithDefaults(options.force);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
program
|
|
83
|
+
.command('sync-script')
|
|
84
|
+
.description('Sync a script name from global or local package.json')
|
|
85
|
+
.addArgument(new commander.Argument('<name>', 'script name in package.json'))
|
|
86
|
+
.addArgument(new commander.Argument('<to>', 'global | local'))
|
|
87
|
+
.action((name, to) => {
|
|
88
|
+
if (to !== 'global' && to !== 'local') {
|
|
89
|
+
logger.error("Source must be either 'global' or 'local'.");
|
|
90
|
+
return;
|
|
70
91
|
}
|
|
92
|
+
commands.scripts.sync(name, to);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
program
|
|
96
|
+
.command('open-solution')
|
|
97
|
+
.description('Open solution folder')
|
|
98
|
+
.action(() => {
|
|
99
|
+
commands.openSolution();
|
|
100
|
+
});
|
|
71
101
|
|
|
72
|
-
|
|
102
|
+
program
|
|
103
|
+
.command('set-heft-dotenv-plugin')
|
|
104
|
+
.description('Installs and configures npm package that allows you to use .env files with Heft')
|
|
105
|
+
.action(() => {
|
|
106
|
+
util.installHeftDotenvPlugin();
|
|
73
107
|
});
|
|
74
108
|
|
|
75
109
|
program
|
|
@@ -1,13 +0,0 @@
|
|
|
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
|
-
};
|