create-powerapps-project 0.19.1 → 0.21.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.
@@ -1,55 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.install = exports.getYarn = void 0;
3
+ exports.install = void 0;
4
4
  /* eslint-disable @typescript-eslint/no-explicit-any */
5
5
  const child_process_1 = require("child_process");
6
- const getEnvInfo_1 = require("./getEnvInfo");
7
- const getYarn = () => {
8
- const yarnInfo = (0, getEnvInfo_1.getEnvInfo)().Binaries.Yarn;
9
- return yarnInfo && yarnInfo.path;
10
- };
11
- exports.getYarn = getYarn;
12
- const getNpm = () => {
13
- const npmInfo = (0, getEnvInfo_1.getEnvInfo)().Binaries.npm;
14
- return npmInfo && npmInfo.path;
15
- };
16
- const install = (cwd, type) => {
17
- const packages = getPackages(type);
18
- if (process.env.JEST_WORKER_ID !== undefined) {
6
+ const install = (cwd, type, packageManager) => {
7
+ if (process.env.JEST_WORKER_ID != undefined) {
19
8
  return;
20
9
  }
21
- if ((0, exports.getYarn)()) {
22
- (0, child_process_1.spawnSync)((0, exports.getYarn)(), ['add', ...packages.devDependencies], { stdio: 'inherit', cwd });
23
- if (packages.dependencies) {
24
- (0, child_process_1.spawnSync)((0, exports.getYarn)(), ['add', ...packages.dependencies], { stdio: 'inherit', cwd });
25
- }
10
+ if (type === 'pcf') {
11
+ (0, child_process_1.spawnSync)(packageManager, ['install'], { stdio: 'inherit', shell: true });
26
12
  }
27
13
  else {
28
- (0, child_process_1.spawnSync)(getNpm(), ['install', ...packages.devDependencies], { stdio: 'inherit', cwd });
14
+ const packages = getPackages(type);
15
+ (0, child_process_1.spawnSync)(packageManager, ['add', ...packages.devDependencies], { stdio: 'inherit', shell: true });
29
16
  if (packages.dependencies) {
30
- (0, child_process_1.spawnSync)(getNpm(), ['install', ...packages.dependencies], { stdio: 'inherit', cwd });
17
+ (0, child_process_1.spawnSync)(packageManager, ['add', ...packages.dependencies], { stdio: 'inherit', shell: true });
31
18
  }
32
19
  }
33
20
  };
34
21
  exports.install = install;
35
22
  function getPackages(type) {
36
- if (type === 'pcf') {
37
- return {
38
- dependencies: [
39
- 'react@17.0.2',
40
- 'react-dom@17.0.2',
41
- '@fluentui/react',
42
- '@fluentui/font-icons-mdl2'
43
- ],
44
- devDependencies: [
45
- //`powerapps-project-${type}`,
46
- '@types/react@17.0.39',
47
- '@types/react-dom@17.0.11',
48
- '@types/xrm',
49
- '-D'
50
- ]
51
- };
52
- }
53
23
  const packages = {
54
24
  devDependencies: [
55
25
  `powerapps-project-${type}`,
package/lib/plopfile.js CHANGED
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -27,76 +31,24 @@ const child_process_1 = require("child_process");
27
31
  const fs_1 = __importDefault(require("fs"));
28
32
  const nuget = __importStar(require("./nuget"));
29
33
  const pkg = __importStar(require("./packageManager"));
30
- const getEnvInfo_1 = require("./getEnvInfo");
31
34
  const didSucceed = (code) => `${code}` === '0';
35
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
36
+ const version = require('../package').version;
32
37
  /* eslint-disable @typescript-eslint/no-explicit-any */
33
38
  exports.default = (plop) => {
34
- void (0, getEnvInfo_1.initialize)();
35
- plop.setActionType('signAssembly', (answers) => {
36
- const keyPath = path_1.default.resolve(process.cwd(), `${answers.name}.snk`);
37
- return new Promise((resolve, reject) => {
38
- if (process.env.JEST_WORKER_ID !== undefined) {
39
- resolve('Testing so no need to sign');
40
- }
41
- const sign = (0, child_process_1.spawn)(path_1.default.resolve(__dirname, '../', 'bin', 'sn.exe'), ['-q', '-k', keyPath], { stdio: 'inherit' });
42
- sign.on('close', (code) => {
43
- if (didSucceed(code)) {
44
- resolve('signed assembly');
45
- }
46
- else {
47
- reject('failed to sign assembly');
48
- }
49
- });
50
- sign.on('error', () => {
51
- reject('failed to sign assembly');
52
- });
53
- });
54
- });
55
- plop.setActionType('runPcf', (answers) => {
56
- const args = ['pcf', 'init', '-ns', answers.namespace, '-n', answers.name, '-t', answers.template];
57
- /// Setting framework to React currently unsupported by PCF CLI
58
- // if (answers.react) {
59
- // args.push('-fw', 'react');
60
- // }
61
- if (process.env.JEST_WORKER_ID !== undefined) {
62
- args.push('-npm', 'false');
63
- }
64
- return new Promise((resolve, reject) => {
65
- const pac = (0, child_process_1.spawn)('pac', args, { stdio: 'inherit' });
66
- pac.on('close', (code) => {
67
- if (didSucceed(code)) {
68
- resolve('pcf project created');
69
- }
70
- else {
71
- reject('Ensure the Power Platform CLI is installed. Command must be run from within VS Code if using the Power Platform Extension');
72
- }
73
- });
74
- pac.on('error', () => {
75
- reject('Ensure the Power Platform CLI is installed. Command must be run from within VS Code if using the Power Platform Extension');
76
- });
77
- });
78
- });
79
- plop.setActionType('addGenScript', async () => {
80
- const packagePath = path_1.default.resolve(process.cwd(), 'package.json');
81
- // eslint-disable-next-line @typescript-eslint/no-var-requires
82
- const packageJson = require(packagePath);
83
- packageJson.scripts.gen = 'plop';
84
- await fs_1.default.promises.writeFile(packagePath, JSON.stringify(packageJson, null, 4), 'utf8');
85
- return 'added plop script to package.json';
86
- });
87
- plop.setActionType('nugetInstall', async (answers) => {
88
- const xrmVersions = await nuget.getNugetPackageVersions('JourneyTeam.Xrm');
89
- const xrmVersion = xrmVersions.shift();
90
- nuget.install(answers.name, answers.sdkVersion, xrmVersion);
91
- return 'installed nuget packages';
92
- });
93
- plop.setActionType('npmInstall', (_answers, config) => {
94
- if (config?.projectType) {
95
- pkg.install(process.cwd(), config.projectType);
96
- }
97
- return 'installed npm packages';
98
- });
99
- const connectionQuestions = [
39
+ plop.setWelcomeMessage(`Creating new Dataverse project using create-powerapps-project v${version}. Please choose type of project to create.`);
40
+ const packageQuestion = {
41
+ type: 'list',
42
+ name: 'package',
43
+ message: 'package manager (ensure selected option is installed)',
44
+ choices: [
45
+ { name: 'npm', value: 'npm' },
46
+ { name: 'pnpm', value: 'pnpm' },
47
+ { name: 'yarn', value: 'yarn' }
48
+ ],
49
+ default: 'npm'
50
+ };
51
+ const sharedQuestions = [
100
52
  {
101
53
  type: 'input',
102
54
  name: 'server',
@@ -114,6 +66,18 @@ exports.default = (plop) => {
114
66
  message: 'dataverse solution unique name:'
115
67
  }
116
68
  ];
69
+ plop.setActionType('addScript', async (answers) => {
70
+ const packagePath = path_1.default.resolve(process.cwd(), 'package.json');
71
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
72
+ const packageJson = require(packagePath);
73
+ packageJson.scripts[answers.scriptKey] = answers.scriptValue;
74
+ await fs_1.default.promises.writeFile(packagePath, JSON.stringify(packageJson, null, 4), 'utf8');
75
+ return `added ${answers.scriptKey} script to package.json`;
76
+ });
77
+ plop.setActionType('npmInstall', (answers) => {
78
+ pkg.install(process.cwd(), answers.projectType, answers.package);
79
+ return 'installed npm packages';
80
+ });
117
81
  plop.setGenerator('assembly', {
118
82
  description: 'generate dataverse assembly project',
119
83
  prompts: [
@@ -129,7 +93,20 @@ exports.default = (plop) => {
129
93
  {
130
94
  type: 'input',
131
95
  name: 'name',
132
- message: 'default C# namespace (Company.Crm.Plugins):'
96
+ message: 'default C# namespace (Company.Crm.Plugins):',
97
+ validate: (name) => {
98
+ const namespace = name.split('.');
99
+ if (namespace.length !== 3) {
100
+ return `enter namespace using 'Company.Crm.Plugins' convention`;
101
+ }
102
+ for (const item of namespace) {
103
+ const title = plop.renderString('{{titleCase name}}', { name: item });
104
+ if (title !== item) {
105
+ return `enter namespace using pascal case (Company.Crm.Plugins)`;
106
+ }
107
+ }
108
+ return true;
109
+ }
133
110
  },
134
111
  {
135
112
  type: 'list',
@@ -147,7 +124,8 @@ exports.default = (plop) => {
147
124
  }
148
125
  ]
149
126
  },
150
- ...connectionQuestions,
127
+ packageQuestion,
128
+ ...sharedQuestions,
151
129
  ],
152
130
  actions: [
153
131
  {
@@ -170,15 +148,39 @@ exports.default = (plop) => {
170
148
  destination: process.cwd(),
171
149
  force: true
172
150
  },
173
- {
174
- type: 'signAssembly'
151
+ (answers) => {
152
+ const keyPath = path_1.default.resolve(process.cwd(), `${answers.name}.snk`);
153
+ return new Promise((resolve, reject) => {
154
+ if (process.env.JEST_WORKER_ID !== undefined) {
155
+ resolve('Testing so no need to sign');
156
+ }
157
+ else {
158
+ const sign = (0, child_process_1.spawn)(path_1.default.resolve(__dirname, '..', 'bin', 'sn.exe'), ['-q', '-k', keyPath], { stdio: 'inherit' });
159
+ sign.on('close', (code) => {
160
+ if (didSucceed(code)) {
161
+ resolve('signed assembly');
162
+ }
163
+ else {
164
+ reject('failed to sign assembly');
165
+ }
166
+ });
167
+ sign.on('error', () => {
168
+ reject('failed to sign assembly');
169
+ });
170
+ }
171
+ });
175
172
  },
176
- {
177
- type: 'nugetInstall'
173
+ async (answers) => {
174
+ const xrmVersions = await nuget.getNugetPackageVersions('JourneyTeam.Xrm');
175
+ const xrmVersion = xrmVersions.shift();
176
+ nuget.install(answers.name, answers.sdkVersion, xrmVersion);
177
+ return 'installed nuget packages';
178
178
  },
179
179
  {
180
180
  type: 'npmInstall',
181
- projectType: 'assembly'
181
+ data: {
182
+ projectType: 'assembly'
183
+ }
182
184
  }
183
185
  ]
184
186
  });
@@ -208,52 +210,95 @@ exports.default = (plop) => {
208
210
  type: 'confirm',
209
211
  name: 'react',
210
212
  message: 'use react?'
211
- }
213
+ },
214
+ packageQuestion
212
215
  ],
213
216
  actions: [
217
+ (answers) => {
218
+ const args = ['pcf', 'init', '-ns', answers.namespace, '-n', answers.name, '-t', answers.template];
219
+ // Set framework to React if selected
220
+ if (answers.react) {
221
+ args.push('-fw', 'react');
222
+ }
223
+ if (process.env.JEST_WORKER_ID !== undefined || answers.package !== 'npm') {
224
+ args.push('-npm', 'false');
225
+ }
226
+ else {
227
+ args.push('-npm', 'true');
228
+ }
229
+ return new Promise((resolve, reject) => {
230
+ const pac = (0, child_process_1.spawn)('pac', args, { stdio: 'inherit' });
231
+ pac.on('close', (code) => {
232
+ if (didSucceed(code)) {
233
+ resolve('pcf project created');
234
+ }
235
+ else {
236
+ reject('Ensure the Power Platform CLI is installed. Command must be run from within Visual Studio Code if using the Power Platform Extension');
237
+ }
238
+ });
239
+ pac.on('error', () => {
240
+ reject('Ensure the Power Platform CLI is installed. Command must be run from within Visual Studio Code if using the Power Platform Extension');
241
+ });
242
+ });
243
+ },
214
244
  {
215
- type: 'runPcf'
245
+ type: 'add',
246
+ templateFile: '../plop-templates/pcf/tsconfig.json',
247
+ path: path_1.default.resolve(process.cwd(), 'tsconfig.json'),
248
+ force: true
216
249
  },
217
250
  {
218
251
  type: 'addMany',
219
252
  templateFiles: [
220
- '../plop-templates/pcf/App.tsx',
221
- '../plop-templates/pcf/index.ts.hbs'
253
+ '../plop-templates/pcf/App.tsx.hbs',
254
+ '../plop-templates/pcf/AppContext.ts'
222
255
  ],
223
256
  base: '../plop-templates/pcf',
224
257
  destination: `${process.cwd()}/{{ name }}`,
225
- force: true,
226
258
  skip: (answers) => {
227
259
  if (!answers.react) {
228
260
  return 'react not included';
229
261
  }
262
+ return;
230
263
  }
231
264
  },
232
265
  {
233
- type: 'add',
234
- templateFile: '../plop-templates/pcf/tsconfig.json',
235
- path: path_1.default.resolve(process.cwd(), 'tsconfig.json'),
236
- force: true,
237
- skip: (answers) => {
238
- if (!answers.react) {
239
- return 'react not included';
240
- }
241
- }
266
+ type: 'modify',
267
+ path: `${process.cwd()}/{{ name }}/index.ts`,
268
+ pattern: 'import { HelloWorld, IHelloWorldProps } from "./HelloWorld";',
269
+ template: `import { App, IAppProps } from './App';`
242
270
  },
243
271
  {
244
- type: 'addGenScript',
245
- skip: (answers) => {
246
- if (!answers.react) {
247
- return 'react not included';
248
- }
272
+ type: 'modify',
273
+ path: `${process.cwd()}/{{ name }}/index.ts`,
274
+ pattern: 'HelloWorld, props',
275
+ template: 'App, props'
276
+ },
277
+ {
278
+ type: 'modify',
279
+ path: `${process.cwd()}/{{ name }}/index.ts`,
280
+ pattern: `const props: IHelloWorldProps = { name: 'Hello, World!' };`,
281
+ template: `const props: IAppProps = { context: context };`
282
+ },
283
+ {
284
+ type: 'addScript',
285
+ data: {
286
+ scriptKey: 'build:prod',
287
+ scriptValue: 'pcf-scripts build --buildMode production'
249
288
  }
250
289
  },
290
+ async (answers) => {
291
+ await fs_1.default.promises.rm(path_1.default.resolve(process.cwd(), answers.name, 'HelloWorld.tsx'));
292
+ return 'removed HelloWorld component';
293
+ },
251
294
  {
252
295
  type: 'npmInstall',
253
- projectType: 'pcf',
296
+ data: {
297
+ projectType: 'pcf'
298
+ },
254
299
  skip: (answers) => {
255
- if (!answers.react) {
256
- return 'react not included';
300
+ if (answers.package === 'npm') {
301
+ return 'npm packages already installed';
257
302
  }
258
303
  }
259
304
  }
@@ -273,7 +318,8 @@ exports.default = (plop) => {
273
318
  name: 'namespace',
274
319
  message: 'namespace for form and ribbon scripts:'
275
320
  },
276
- ...connectionQuestions
321
+ packageQuestion,
322
+ ...sharedQuestions
277
323
  ],
278
324
  actions: [
279
325
  {
@@ -282,6 +328,12 @@ exports.default = (plop) => {
282
328
  base: '../plop-templates/webresource',
283
329
  destination: process.cwd(),
284
330
  force: true
331
+ },
332
+ {
333
+ type: 'npmInstall',
334
+ data: {
335
+ projectType: 'webresource'
336
+ }
285
337
  }
286
338
  ]
287
339
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-powerapps-project",
3
3
  "description": "💧 plop generator for Dataverse development",
4
- "version": "0.19.1",
4
+ "version": "0.21.1",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
7
7
  "main": "lib/index.js",
@@ -23,11 +23,10 @@
23
23
  "clean": "rimraf lib"
24
24
  },
25
25
  "dependencies": {
26
- "envinfo": "^7.8.1",
27
26
  "plop": "^2.7.6"
28
27
  },
29
28
  "devDependencies": {
30
- "@types/envinfo": "^7.8.1",
31
- "@types/node": "^14.14.21"
29
+ "@types/node": "^14.14.21",
30
+ "node-plop": "^0.26.3"
32
31
  }
33
32
  }
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import { IInputs } from './generated/ManifestTypes';
3
+ import AppContext from './AppContext';
4
+
5
+ export interface IAppProps {
6
+ context: ComponentFramework.Context<IInputs>;
7
+ }
8
+
9
+ export const App = React.memo((props: IAppProps) => {
10
+ const {
11
+ context,
12
+ } = props;
13
+
14
+ return (
15
+ <AppContext.Provider value=\{{ context: context }}>
16
+ </AppContext.Provider>
17
+ );
18
+ });
19
+
20
+ App.displayName = 'App';
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { IInputs } from './generated/ManifestTypes';
3
+
4
+ interface IAppContext {
5
+ context: ComponentFramework.Context<IInputs>;
6
+ }
7
+
8
+ const AppContext = React.createContext<IAppContext>({} as IAppContext);
9
+
10
+ export default AppContext;
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "extends": "./node_modules/pcf-scripts/tsconfig_base.json",
3
3
  "compilerOptions": {
4
- "target": "ES6",
5
- "esModuleInterop": true
4
+ "typeRoots": ["node_modules/@types"],
5
+ "esModuleInterop": true,
6
+ "target": "ES6"
6
7
  }
7
8
  }
package/lib/getEnvInfo.js DELETED
@@ -1,33 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.initialize = exports.getEnvInfo = void 0;
7
- /* eslint-disable @typescript-eslint/no-explicit-any */
8
- const envinfo_1 = __importDefault(require("envinfo"));
9
- const os_1 = __importDefault(require("os"));
10
- const path_1 = __importDefault(require("path"));
11
- let envInfoCache;
12
- const getEnvInfo = () => {
13
- return envInfoCache;
14
- };
15
- exports.getEnvInfo = getEnvInfo;
16
- const initialize = async () => {
17
- envInfoCache = JSON.parse(await envinfo_1.default.run({
18
- Binaries: ['Yarn', 'npm']
19
- }, { json: true, showNotFound: false }));
20
- if (envInfoCache.Binaries.Yarn) {
21
- envInfoCache.Binaries.Yarn.path = expandHome(envInfoCache.Binaries.Yarn.path);
22
- }
23
- if (envInfoCache.Binaries.npm) {
24
- envInfoCache.Binaries.npm.path = expandHome(envInfoCache.Binaries.npm.path);
25
- }
26
- };
27
- exports.initialize = initialize;
28
- const expandHome = (pathString) => {
29
- if (pathString.startsWith('~' + path_1.default.sep)) {
30
- return pathString.replace('~', os_1.default.homedir());
31
- }
32
- return pathString;
33
- };
@@ -1,20 +0,0 @@
1
- import React from 'react';
2
- import { ThemeProvider } from '@fluentui/react/lib/utilities/ThemeProvider/ThemeProvider';
3
-
4
- export interface AppProps {
5
- isTestHarness: boolean;
6
- }
7
-
8
- export const App = React.memo((props: AppProps) => {
9
- const {
10
- isTestHarness
11
- } = props;
12
-
13
- return (
14
- <ThemeProvider dir='ltr'>
15
- </ThemeProvider>
16
- );
17
- });
18
-
19
- App.displayName = 'App';
20
-
@@ -1,70 +0,0 @@
1
- import { IInputs, IOutputs } from "./generated/ManifestTypes";
2
- import React from 'react';
3
- import ReactDOM from 'react-dom';
4
- import { initializeIcons } from '@fluentui/font-icons-mdl2';
5
-
6
- import { App, AppProps } from './App';
7
-
8
- export class {{name}} implements ComponentFramework.StandardControl<IInputs, IOutputs> {
9
- container: HTMLDivElement;
10
- root: Root;
11
- context: ComponentFramework.Context<IInputs>;
12
- isTestHarness: boolean;
13
- props: AppProps;
14
-
15
- /**
16
- * Empty constructor.
17
- */
18
- constructor() {
19
-
20
- }
21
-
22
- /**
23
- * Used to initialize the control instance. Controls can kick off remote server calls and other initialization actions here.
24
- * Data-set values are not initialized here, use updateView.
25
- * @param context The entire property bag available to control via Context Object; It contains values as set up by the customizer mapped to property names defined in the manifest, as well as utility functions.
26
- * @param notifyOutputChanged A callback method to alert the framework that the control has new outputs ready to be retrieved asynchronously.
27
- * @param state A piece of data that persists in one session for a single user. Can be set at any point in a controls life cycle by calling 'setControlState' in the Mode interface.
28
- * @param container If a control is marked control-type='standard', it will receive an empty div element within which it can render its content.
29
- */
30
- public init(context: ComponentFramework.Context<IInputs>, notifyOutputChanged: () => void, state: ComponentFramework.Dictionary, container: HTMLDivElement): void {
31
- initializeIcons(undefined, { disableWarnings: true });
32
-
33
- this.container = container;
34
- this.context = context;
35
- this.isTestHarness = document.getElementById('control-dimensions') !== null;
36
- }
37
-
38
-
39
- /**
40
- * Called when any value in the property bag has changed. This includes field values, data-sets, global values such as container height and width, offline status, control metadata values such as label, visible, etc.
41
- * @param context The entire property bag available to control via Context Object; It contains values as set up by the customizer mapped to names defined in the manifest, as well as utility functions
42
- */
43
- public updateView(context: ComponentFramework.Context<IInputs>): void {
44
- this.props = {
45
- isTestHarness: this.isTestHarness
46
- };
47
-
48
- ReactDOM.render(
49
- React.createElement(App, this.props),
50
- this.container
51
- );
52
- }
53
-
54
- /**
55
- * It is called by the framework prior to a control receiving new data.
56
- * @returns an object based on nomenclature defined in manifest, expecting object[s] for property marked as “bound” or “output”
57
- */
58
- public getOutputs(): IOutputs {
59
- return {};
60
- }
61
-
62
- /**
63
- * Called when the control is to be removed from the DOM tree. Controls should use this call for cleanup.
64
- * i.e. cancelling any pending remote calls, removing listeners, etc.
65
- */
66
- public destroy(): void {
67
- ReactDOM.unmountComponentAtNode(this.container);
68
- }
69
-
70
- }