Skip to content

Commit

Permalink
Added the 'pa pcf init' command solving pnp#952
Browse files Browse the repository at this point in the history
  • Loading branch information
YannickRe authored and waldekmastykarz committed Sep 8, 2019
1 parent 117e320 commit 742b8fc
Show file tree
Hide file tree
Showing 28 changed files with 1,495 additions and 5 deletions.
47 changes: 47 additions & 0 deletions docs/manual/docs/cmd/pa/pcf/pcf-init.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# pa pcf init

Creates new PowerApps component framework project

## Usage

```sh
pa pcf init [options]
```

## Options

Option|Description
------|-----------
`--help`|output usage information
`--namespace <namespace>`|The namespace for the component.
`--name <name>`|The name for the component.
`--template <template>`|Choose a template for the component. `Field|Dataset`.
`-o, --output [output]`|Output type. `json|text`. Default `text`
`--verbose`|Runs command with verbose logging
`--debug`|Runs command with debug logging

## Remarks

Name cannot contain reserved Javascript words. Only characters within the ranges [A-Z], [a-z] or [0-9] are allowed. The first character may not be a number.

Namespace cannot contain reserved Javascript words. Only characters within the ranges [A-Z], [a-z], [0-9], or '.' are allowed. The first and last character may not be the '.' character. Consecutive '.' characters are not allowed. Numbers are not allowed as the first character or immediately after a period.

Template currently only supports Field or Dataset.

## Examples

Initialize the PowerApps Component Framework for a Field component

```sh
pa pcf init --namespace yourNamespace --name yourCustomFieldComponent --template Field
```

Initialize the PowerApps Component Framework for a Dataset component

```sh
pa pcf init --namespace yourNamespace --name yourCustomFieldComponent --template Dataset
```

## More information

- Create and build a custom component: [https://docs.microsoft.com/en-us/powerapps/developer/component-framework/create-custom-controls-using-pcf](https://docs.microsoft.com/en-us/powerapps/developer/component-framework/create-custom-controls-using-pcf)
3 changes: 3 additions & 0 deletions docs/manual/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ nav:
- schemaextension get: 'cmd/graph/schemaextension/schemaextension-get.md'
- Outlook (outlook):
- outlook sendmail: 'cmd/outlook/sendmail.md'
- PowerApps (pa):
- pcf:
- pcf init: 'cmd/pa/pcf/pcf-init.md'
- Microsoft Planner (planner):
- task:
- task list: 'cmd/planner/task/task-list.md'
Expand Down
28 changes: 27 additions & 1 deletion scripts/copy-files.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,30 @@ if (!fs.existsSync(assetsDir)) {
fs.mkdirSync(assetsDir);
}
fs.copyFileSync('src/o365/spfx/commands/project/project-upgrade/assets/tab20x20.png', path.join(assetsDir, 'tab20x20.png'));
fs.copyFileSync('src/o365/spfx/commands/project/project-upgrade/assets/tab96x96.png', path.join(assetsDir, 'tab96x96.png'));
fs.copyFileSync('src/o365/spfx/commands/project/project-upgrade/assets/tab96x96.png', path.join(assetsDir, 'tab96x96.png'));

const paPcfAssetsSourceDir = 'src/o365/pa/commands/pcf/pcf-init/assets';
const paPcfAssetsDir = 'dist/o365/pa/commands/pcf/pcf-init/assets';
if (!fs.existsSync(paPcfAssetsDir)) {
fs.mkdirSync(paPcfAssetsDir);
}

const getFilePaths = (folderPath) => {
const entryPaths = fs.readdirSync(folderPath).map(entry => path.join(folderPath, entry));
const filePaths = entryPaths.filter(entryPath => fs.statSync(entryPath).isFile());
const dirPaths = entryPaths.filter(entryPath => !filePaths.includes(entryPath));
const dirFiles = dirPaths.reduce((prev, curr) => prev.concat(getFilePaths(curr)), []);
return [...filePaths, ...dirFiles];
};

getFilePaths(paPcfAssetsSourceDir).forEach(file => {
const fileName = path.basename(file);
const filePath = path.relative(paPcfAssetsSourceDir, path.dirname(file));
const destinationFilePath = path.join(paPcfAssetsDir, filePath);

if (!fs.existsSync(destinationFilePath)) {
fs.mkdirSync(destinationFilePath);
}

fs.copyFileSync(file, path.join(destinationFilePath, fileName));
});
41 changes: 38 additions & 3 deletions src/Utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import { CommandError } from './Command';
import * as os from 'os';

describe('Utils', () => {
it('isValidISODate returns true if value is in ISO Date format with - seperator', () => {
it('isValidISODate returns true if value is in ISO Date format with - separator', () => {
const result = Utils.isValidISODate("2019-03-22");
assert.equal(result, true);
});

it('isValidISODate returns true if value is in ISO Date format with . seperator', () => {
it('isValidISODate returns true if value is in ISO Date format with . separator', () => {
const result = Utils.isValidISODate("2019.03.22");
assert.equal(result, true);
});

it('isValidISODate returns true if value is in ISO Date format with / seperator', () => {
it('isValidISODate returns true if value is in ISO Date format with / separator', () => {
const result = Utils.isValidISODate("2019/03/22");
assert.equal(result, true);
});
Expand Down Expand Up @@ -810,4 +810,39 @@ describe('Utils', () => {
const actual = Utils.getUserNameFromAccessToken('');
assert.equal(actual, '');
});

it('isJavascriptReservedWord returns true if value equals a JavaScript Reserved Word (eg. onload)', () => {
const result = Utils.isJavascriptReservedWord('onload');
assert.strictEqual(result, true);
});

it('isJavascriptReservedWord returns false if value doesn\'t equal a JavaScript Reserved Word due to case sensitivity (eg. onLoad)', () => {
const result = Utils.isJavascriptReservedWord('onLoad');
assert.strictEqual(result, false);
});

it('isJavascriptReservedWord returns false if value doesn\'t equal a JavaScript Reserved Word', () => {
const result = Utils.isJavascriptReservedWord('exampleword');
assert.strictEqual(result, false);
});

it('isJavascriptReservedWord returns false if value contains but doesn\'t equal a JavaScript Reserved Word (eg. encodeURIComponent)', () => {
const result = Utils.isJavascriptReservedWord('examplewordencodeURIComponent');
assert.strictEqual(result, false);
});

it('isJavascriptReservedWord returns true if any part of a value, when split on dot, equals a JavaScript Reserved Word (eg. innerHeight)', () => {
const result = Utils.isJavascriptReservedWord('exampleword.innerHeight.anotherpart');
assert.strictEqual(result, true);
});

it('isJavascriptReservedWord returns false if any part of a value, when split on dot, doesn\'t equal a JavaScript Reserved Word', () => {
const result = Utils.isJavascriptReservedWord('exampleword.secondsection.anotherpart');
assert.strictEqual(result, false);
});

it('isJavascriptReservedWord returns false if any part of a value, when split on dot, contains but doesn\'t equal a JavaScript Reserved Word (eg. layer)', () => {
const result = Utils.isJavascriptReservedWord('exampleword.layersecondsection.anotherpart');
assert.strictEqual(result, false);
});
});
171 changes: 171 additions & 0 deletions src/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,4 +333,175 @@ export default class Utils {
}
return `${tenantUrl}${serverRelativeUrl}`;
}

public static isJavascriptReservedWord(input: string) {
const javascriptReservedWords: string[] = [
"arguments",
"await",
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"enum",
"eval",
"export",
"extends",
"false",
"finally",
"for",
"function",
"if",
"implements",
"import",
"in",
"instanceof",
"interface",
"let",
"new",
"null",
"package",
"private",
"protected",
"public",
"return",
"static",
"super",
"switch",
"this",
"throw",
"true",
"try",
"typeof",
"var",
"void",
"while",
"with",
"yield",
"Array",
"Date",
"eval",
"function",
"hasOwnProperty",
"Infinity",
"isFinite",
"isNaN",
"isPrototypeOf",
"length",
"Math",
"NaN",
"name",
"Number",
"Object",
"prototype",
"String",
"toString",
"undefined",
"valueOf",
"alert",
"all",
"anchor",
"anchors",
"area",
"assign",
"blur",
"button",
"checkbox",
"clearInterval",
"clearTimeout",
"clientInformation",
"close",
"closed",
"confirm",
"constructor",
"crypto",
"decodeURI",
"decodeURIComponent",
"defaultStatus",
"document",
"element",
"elements",
"embed",
"embeds",
"encodeURI",
"encodeURIComponent",
"escape",
"event",
"fileUpload",
"focus",
"form",
"forms",
"frame",
"innerHeight",
"innerWidth",
"layer",
"layers",
"link",
"location",
"mimeTypes",
"navigate",
"navigator",
"frames",
"frameRate",
"hidden",
"history",
"image",
"images",
"offscreenBuffering",
"open",
"opener",
"option",
"outerHeight",
"outerWidth",
"packages",
"pageXOffset",
"pageYOffset",
"parent",
"parseFloat",
"parseInt",
"password",
"pkcs11",
"plugin",
"prompt",
"propertyIsEnum",
"radio",
"reset",
"screenX",
"screenY",
"scroll",
"secure",
"select",
"self",
"setInterval",
"setTimeout",
"status",
"submit",
"taint",
"text",
"textarea",
"top",
"unescape",
"untaint",
"window",
"onblur",
"onclick",
"onerror",
"onfocus",
"onkeydown",
"onkeypress",
"onkeyup",
"onmouseover",
"onload",
"onmouseup",
"onmousedown",
"onsubmit"
];
return input && !input.split('.').every(value => !~javascriptReservedWords.indexOf(value));
}
}
5 changes: 5 additions & 0 deletions src/o365/pa/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const prefix: string = 'pa';

export default {
PCF_INIT: `${prefix} pcf init`
};
Loading

0 comments on commit 742b8fc

Please sign in to comment.