diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8785a071..9aad7624 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -116,6 +116,7 @@ The scope must be one of the following:
* ionic
* nativescript
* nest
+* elements
* helpers
#### Subject
diff --git a/src/app.electron/index.ts b/src/app.electron/index.ts
index 6f892961..2a1908e2 100644
--- a/src/app.electron/index.ts
+++ b/src/app.electron/index.ts
@@ -13,12 +13,12 @@ import {
noop
} from '@angular-devkit/schematics';
import { Schema as ApplicationOptions } from './schema';
-import { prerun, getPrefix, getNpmScope, stringUtils, addRootDeps, updateAngularProjects, updateNxProjects, formatFiles, getJsonFromFile, updatePackageScripts, addPostinstallers, applyAppNamingConvention, getGroupByName, getAppName, missingNameArgument } from '../utils';
+import { prerun, getPrefix, getNpmScope, stringUtils, addRootDeps, updateAngularProjects, updateNxProjects, formatFiles, getJsonFromFile, updatePackageScripts, addPostinstallers, applyAppNamingConvention, getGroupByName, getAppName, missingArgument } from '../utils';
export default function (options: ApplicationOptions) {
if (!options.name) {
throw new SchematicsException(
- missingNameArgument('Provide a name for your Electron app.', 'ng g app.electron sample')
+ missingArgument('name', 'Provide a name for your Electron app.', 'ng g app.electron sample')
);
}
if (!options.target) {
diff --git a/src/app.ionic/index.ts b/src/app.ionic/index.ts
index 5eae902a..9375de52 100644
--- a/src/app.ionic/index.ts
+++ b/src/app.ionic/index.ts
@@ -13,13 +13,13 @@ import {
schematic,
noop,
} from '@angular-devkit/schematics';
-import { stringUtils, prerun, getNpmScope, getPrefix, addRootDeps, updatePackageScripts, updateAngularProjects, updateNxProjects, formatFiles, applyAppNamingConvention, getAppName, missingNameArgument } from '../utils';
+import { stringUtils, prerun, getNpmScope, getPrefix, addRootDeps, updatePackageScripts, updateAngularProjects, updateNxProjects, formatFiles, applyAppNamingConvention, getAppName, missingArgument } from '../utils';
import { Schema as ApplicationOptions } from './schema';
export default function (options: ApplicationOptions) {
if (!options.name) {
throw new SchematicsException(
- missingNameArgument('Provide a name for your Ionic app.', 'ng g app.ionic sample')
+ missingArgument('name', 'Provide a name for your Ionic app.', 'ng g app.ionic sample')
);
}
diff --git a/src/app.nativescript/index.ts b/src/app.nativescript/index.ts
index f57cfa98..a9c41f3a 100644
--- a/src/app.nativescript/index.ts
+++ b/src/app.nativescript/index.ts
@@ -13,13 +13,13 @@ import {
schematic,
noop,
} from '@angular-devkit/schematics';
-import { stringUtils, prerun, getNpmScope, getPrefix, addRootDeps, updatePackageScripts, updateAngularProjects, updateNxProjects, applyAppNamingConvention, getGroupByName, getAppName, missingNameArgument } from '../utils';
+import { stringUtils, prerun, getNpmScope, getPrefix, addRootDeps, updatePackageScripts, updateAngularProjects, updateNxProjects, applyAppNamingConvention, getGroupByName, getAppName, missingArgument } from '../utils';
import { Schema as ApplicationOptions } from './schema';
export default function (options: ApplicationOptions) {
if (!options.name) {
throw new SchematicsException(
- missingNameArgument('Provide a name for your NativeScript app.', 'ng g app.nativescript sample')
+ missingArgument('name', 'Provide a name for your NativeScript app.', 'ng g app.nativescript sample')
);
}
if (options.setupSandbox) {
diff --git a/src/app.nest/index.ts b/src/app.nest/index.ts
index 2937aa91..e0fe2114 100644
--- a/src/app.nest/index.ts
+++ b/src/app.nest/index.ts
@@ -28,14 +28,14 @@ import {
prerun,
applyAppNamingConvention,
getAppName,
- missingNameArgument,
- updateJsonInTree
+ updateJsonInTree,
+ missingArgument
} from "../utils";
export default function (options: ApplicationOptions) {
if (!options.name) {
throw new SchematicsException(
- missingNameArgument('Provide a name for your Nest app.', 'ng g app.nest sample')
+ missingArgument('name', 'Provide a name for your Nest app.', 'ng g app.nest sample')
);
}
diff --git a/src/app.web/_routing_files/src/app/features/home/components/home.component.html b/src/app.web/_routing_files/src/app/features/home/components/home.component.html
index 4f004bbb..e99f34ef 100644
--- a/src/app.web/_routing_files/src/app/features/home/components/home.component.html
+++ b/src/app.web/_routing_files/src/app/features/home/components/home.component.html
@@ -1,12 +1,5 @@
-
-
- Welcome to an Angular CLI app built with Nrwl Nx and xplat!
-
-
-
+
-
-
+ <<%= prefix %>-header title="<%= name %>"><%= prefix %>-header>
Nx
diff --git a/src/app.web/index.ts b/src/app.web/index.ts
index 5665e39b..a9b14916 100644
--- a/src/app.web/index.ts
+++ b/src/app.web/index.ts
@@ -24,14 +24,14 @@ import {
applyAppNamingConvention,
updateJsonFile,
formatFiles,
- missingNameArgument
+ missingArgument
} from "../utils";
import { Schema as ApplicationOptions } from "./schema";
export default function(options: ApplicationOptions) {
if (!options.name) {
throw new SchematicsException(
- missingNameArgument('Provide a name for your Web app.', 'ng g app my-app')
+ missingArgument('name', 'Provide a name for your Web app.', 'ng g app my-app')
);
}
// ensure sass is used
@@ -173,32 +173,22 @@ platformBrowserDynamic()
function appCmpHtml(name: string) {
return `
-
-
- Welcome to ${name}!
-
-
- An Angular CLI app built with Nrwl Nx and xplat.
-
-
-
+
-
-
-
-
Nx
-
- An open source toolkit for enterprise Angular applications. Nx is designed to help you create and build enterprise grade
- Angular applications. It provides an opinionated approach to application project structure and patterns.
-
-
Quick Start & Documentation
-
-
Watch a 5-minute video on how to get started with Nx.
-
-
{{'hello' | translate}}
-
Try things out
-
-
Learn more about xplat generators.
-
`;
+ <${getPrefix()}-header title="${name}">${getPrefix()}-header>
+
+
Nx
+
+ An open source toolkit for enterprise Angular applications. Nx is designed to help you create and build enterprise grade
+ Angular applications. It provides an opinionated approach to application project structure and patterns.
+
+
Quick Start & Documentation
+
+
Watch a 5-minute video on how to get started with Nx.
+
+
{{'hello' | translate}}
+
Try things out
+
+
Learn more about xplat.
+
`;
}
function appCmpContent() {
diff --git a/src/collection.json b/src/collection.json
index 4fcba48a..97fc0953 100644
--- a/src/collection.json
+++ b/src/collection.json
@@ -49,6 +49,11 @@
"schema": "./app.web/schema.json",
"description": "Nx web app with xplat support."
},
+ "elements": {
+ "factory": "./elements",
+ "schema": "./elements/schema.json",
+ "description": "Create custom elements for the web."
+ },
"feature": {
"factory": "./feature",
"schema": "./feature/schema.json",
diff --git a/src/component/_ionic_files/__name__/__name__.component.html b/src/component/_ionic_files/__name__/__name__.component.html
index 80cac32a..5d7ad0d8 100644
--- a/src/component/_ionic_files/__name__/__name__.component.html
+++ b/src/component/_ionic_files/__name__/__name__.component.html
@@ -1 +1 @@
-<% if (onlyProject || ignoreBase) { %><%= utils.classify(name) %><% } else { %>{{text}}<% } %>
\ No newline at end of file
+<% if (onlyProject || !createBase) { %><%= utils.classify(name) %><% } else { %>{{text}}<% } %>
\ No newline at end of file
diff --git a/src/component/_ionic_files/__name__/__name__.component.ts b/src/component/_ionic_files/__name__/__name__.component.ts
index 6d35e6b2..379c0d0c 100644
--- a/src/component/_ionic_files/__name__/__name__.component.ts
+++ b/src/component/_ionic_files/__name__/__name__.component.ts
@@ -1,13 +1,13 @@
import { Component } from '@angular/core';
-<% if (onlyProject || ignoreBase) { %>import { BaseComponent } from '@<%= npmScope %>/core';<%
+<% if (onlyProject || !createBase) { %>import { BaseComponent } from '@<%= npmScope %>/core';<%
} else { %>import { <%= utils.classify(name) %>BaseComponent } from '@<%= npmScope %>/features';<% } %>
@Component({
selector: '<%= prefix %>-<%= name %>',
templateUrl: '<%= name %>.component.html'
})
-export class <%= utils.classify(name) %>Component extends <%= onlyProject || ignoreBase ? '' : utils.classify(name) %>BaseComponent {
+export class <%= utils.classify(name) %>Component extends <%= onlyProject || !createBase ? '' : utils.classify(name) %>BaseComponent {
constructor() {
super();
diff --git a/src/component/_nativescript_files/__name__/__name__.component.html b/src/component/_nativescript_files/__name__/__name__.component.html
index df93a12d..fa3f81c2 100644
--- a/src/component/_nativescript_files/__name__/__name__.component.html
+++ b/src/component/_nativescript_files/__name__/__name__.component.html
@@ -1,3 +1,3 @@
- text="<%= utils.classify(name) %>"<% } else { %>[text]="text"<% } %> class="text-center">
+ text="<%= utils.classify(name) %>"<% } else { %>[text]="text"<% } %> class="text-center">
\ No newline at end of file
diff --git a/src/component/_nativescript_files/__name__/__name__.component.ts b/src/component/_nativescript_files/__name__/__name__.component.ts
index 46c8ddea..26e37dd3 100644
--- a/src/component/_nativescript_files/__name__/__name__.component.ts
+++ b/src/component/_nativescript_files/__name__/__name__.component.ts
@@ -1,6 +1,6 @@
import { Component } from '@angular/core';
-<% if (onlyProject || ignoreBase) { %>import { BaseComponent } from '@<%= npmScope %>/core';<%
+<% if (onlyProject || !createBase) { %>import { BaseComponent } from '@<%= npmScope %>/core';<%
} else { %>import { <%= utils.classify(name) %>BaseComponent } from '@<%= npmScope %>/features';<% } %>
@Component({
@@ -8,7 +8,7 @@ import { Component } from '@angular/core';
selector: '<%= prefix %>-<%= name %>',
templateUrl: './<%= name %>.component.html'
})
-export class <%= utils.classify(name) %>Component extends <%= onlyProject || ignoreBase ? '' : utils.classify(name) %>BaseComponent {
+export class <%= utils.classify(name) %>Component extends <%= onlyProject || !createBase ? '' : utils.classify(name) %>BaseComponent {
constructor() {
super();
diff --git a/src/component/_web_files/__name__/__name__.component.html b/src/component/_web_files/__name__/__name__.component.html
index 80cac32a..5d7ad0d8 100644
--- a/src/component/_web_files/__name__/__name__.component.html
+++ b/src/component/_web_files/__name__/__name__.component.html
@@ -1 +1 @@
-<% if (onlyProject || ignoreBase) { %><%= utils.classify(name) %><% } else { %>{{text}}<% } %>
\ No newline at end of file
+<% if (onlyProject || !createBase) { %><%= utils.classify(name) %><% } else { %>{{text}}<% } %>
\ No newline at end of file
diff --git a/src/component/_web_files/__name__/__name__.component.ts b/src/component/_web_files/__name__/__name__.component.ts
index 6d35e6b2..379c0d0c 100644
--- a/src/component/_web_files/__name__/__name__.component.ts
+++ b/src/component/_web_files/__name__/__name__.component.ts
@@ -1,13 +1,13 @@
import { Component } from '@angular/core';
-<% if (onlyProject || ignoreBase) { %>import { BaseComponent } from '@<%= npmScope %>/core';<%
+<% if (onlyProject || !createBase) { %>import { BaseComponent } from '@<%= npmScope %>/core';<%
} else { %>import { <%= utils.classify(name) %>BaseComponent } from '@<%= npmScope %>/features';<% } %>
@Component({
selector: '<%= prefix %>-<%= name %>',
templateUrl: '<%= name %>.component.html'
})
-export class <%= utils.classify(name) %>Component extends <%= onlyProject || ignoreBase ? '' : utils.classify(name) %>BaseComponent {
+export class <%= utils.classify(name) %>Component extends <%= onlyProject || !createBase ? '' : utils.classify(name) %>BaseComponent {
constructor() {
super();
diff --git a/src/component/index.ts b/src/component/index.ts
index d37dccb8..a15c8153 100644
--- a/src/component/index.ts
+++ b/src/component/index.ts
@@ -212,7 +212,7 @@ export default function(options: featureOptions) {
prerun(),
// add component for base libs feature
(tree: Tree, context: SchematicContext) =>
- !options.onlyProject && !options.ignoreBase
+ !options.onlyProject && options.createBase
? addToFeature("component", options, "libs", tree, "_base", true)(
tree,
context
@@ -220,7 +220,7 @@ export default function(options: featureOptions) {
: noop()(tree, context),
// adjust libs barrel for subFolder
(tree: Tree, context: SchematicContext) =>
- options.subFolder && !options.onlyProject && !options.ignoreBase
+ options.subFolder && !options.onlyProject && options.createBase
? adjustBarrelIndex(
"component",
options,
@@ -239,7 +239,7 @@ export default function(options: featureOptions) {
: noop()(tree, context),
// adjust libs barrel
(tree: Tree, context: SchematicContext) =>
- !options.onlyProject && !options.ignoreBase
+ !options.onlyProject && options.createBase
? adjustBarrelIndex(
"component",
options,
diff --git a/src/component/index_spec.ts b/src/component/index_spec.ts
index 5f4b2620..fd471aa2 100644
--- a/src/component/index_spec.ts
+++ b/src/component/index_spec.ts
@@ -14,7 +14,8 @@ describe('component schematic', () => {
const defaultOptions: GenerateOptions = {
name: 'signup',
feature: 'foo',
- platforms: 'nativescript,web'
+ platforms: 'nativescript,web',
+ createBase: true
};
let appTree: Tree;
diff --git a/src/component/schema.d.ts b/src/component/schema.d.ts
index f40c3c2e..1251870b 100644
--- a/src/component/schema.d.ts
+++ b/src/component/schema.d.ts
@@ -21,9 +21,9 @@ export interface Schema {
*/
platforms?: string;
/**
- * Ignore base component generation
+ * Create a base component for maximum cross platform sharing
*/
- ignoreBase?: boolean;
+ createBase?: boolean;
/**
* Schematic processing helpers
*/
diff --git a/src/component/schema.json b/src/component/schema.json
index 328bd91e..a519248e 100644
--- a/src/component/schema.json
+++ b/src/component/schema.json
@@ -33,9 +33,9 @@
"type": "string",
"description": "Target platforms"
},
- "ignoreBase": {
+ "createBase": {
"type": "boolean",
- "description": "Ignore base component generation.",
+ "description": "Create a base component for maximum cross platform sharing.",
"default": false
},
"skipFormat": {
diff --git a/src/elements/_builder_files/builder/elements.ts b/src/elements/_builder_files/builder/elements.ts
new file mode 100644
index 00000000..8d47e48f
--- /dev/null
+++ b/src/elements/_builder_files/builder/elements.ts
@@ -0,0 +1,6 @@
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { <%= utils.classify(name) %>Module } from '../<%= name %>.module';
+
+platformBrowserDynamic()
+ .bootstrapModule(<%= utils.classify(name) %>Module)
+ .catch(err => console.log(err));
diff --git a/src/elements/_builder_files/builder/index.html b/src/elements/_builder_files/builder/index.html
new file mode 100644
index 00000000..57d67081
--- /dev/null
+++ b/src/elements/_builder_files/builder/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+ Custom Elements
+
+
+
+
+
+ <%= htmlElements %>
+
+
\ No newline at end of file
diff --git a/src/elements/_builder_files/builder/polyfills.ts b/src/elements/_builder_files/builder/polyfills.ts
new file mode 100644
index 00000000..662fd655
--- /dev/null
+++ b/src/elements/_builder_files/builder/polyfills.ts
@@ -0,0 +1,81 @@
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/** IE9, IE10 and IE11 requires all of the following polyfills. **/
+// import 'core-js/es6/symbol';
+// import 'core-js/es6/object';
+// import 'core-js/es6/function';
+// import 'core-js/es6/parse-int';
+// import 'core-js/es6/parse-float';
+// import 'core-js/es6/number';
+// import 'core-js/es6/math';
+// import 'core-js/es6/string';
+// import 'core-js/es6/date';
+// import 'core-js/es6/array';
+// import 'core-js/es6/regexp';
+// import 'core-js/es6/map';
+// import 'core-js/es6/weak-map';
+// import 'core-js/es6/set';
+
+/** IE10 and IE11 requires the following for NgClass support on SVG elements */
+// import 'classlist.js'; // Run `npm install --save classlist.js`.
+
+/** IE10 and IE11 requires the following for the Reflect API. */
+// import 'core-js/es6/reflect';
+
+
+/** Evergreen browsers require these. **/
+// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
+import 'core-js/es7/reflect';
+
+
+/**
+ * Web Animations `@angular/platform-browser/animations`
+ * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
+ * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
+ **/
+// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
+
+/**
+ * By default, zone.js will patch all possible macroTask and DomEvents
+ * user can disable parts of macroTask/DomEvents patch by setting following flags
+ */
+
+ // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
+ // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
+ // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
+
+ /*
+ * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
+ * with the following flag, it will bypass `zone.js` patch for IE/Edge
+ */
+// (window as any).__Zone_enable_cross_context_check = true;
+
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js/dist/zone'; // Included with Angular CLI.
+
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
+import '@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js';
\ No newline at end of file
diff --git a/src/elements/_builder_files/builder/tsconfig.elements.json b/src/elements/_builder_files/builder/tsconfig.elements.json
new file mode 100644
index 00000000..ae7dc062
--- /dev/null
+++ b/src/elements/_builder_files/builder/tsconfig.elements.json
@@ -0,0 +1,15 @@
+{
+ "extends": "../../../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../../../dist/out-tsc/xplat/web/elements",
+ "module": "es2015",
+ "types": []
+ },
+ "exclude": [
+ "src/test.ts",
+ "**/*.spec.ts"
+ ],
+ "include": [
+ "**/*.ts"
+ ]
+}
\ No newline at end of file
diff --git a/src/elements/_files/__name__.module.ts b/src/elements/_files/__name__.module.ts
new file mode 100644
index 00000000..7749d4e8
--- /dev/null
+++ b/src/elements/_files/__name__.module.ts
@@ -0,0 +1,20 @@
+import { NgModule, Injector } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { createCustomElement } from '@angular/elements';
+import { <%= componentSymbolList %> } from '<%= barrel %>';
+
+@NgModule({
+ imports: [BrowserModule],
+ entryComponents: [
+ <%= componentSymbolList %>
+ ]
+})
+export class <%= utils.classify(name) %>Module {
+ constructor(private injector: Injector) {
+
+ }
+
+ ngDoBootstrap() {
+ <%= customElementList %>
+ }
+}
diff --git a/src/elements/index.ts b/src/elements/index.ts
new file mode 100644
index 00000000..0e0fcb2c
--- /dev/null
+++ b/src/elements/index.ts
@@ -0,0 +1,308 @@
+import {
+ apply,
+ chain,
+ Tree,
+ Rule,
+ url,
+ move,
+ template,
+ mergeWith,
+ branchAndMerge,
+ SchematicContext,
+ SchematicsException,
+ externalSchematic,
+ noop
+} from "@angular-devkit/schematics";
+
+import {
+ stringUtils,
+ prerun,
+ updatePackageScripts,
+ getNpmScope,
+ getPrefix,
+ getJsonFromFile,
+ applyAppNamingConvention,
+ updateJsonFile,
+ formatFiles,
+ updateJsonInTree,
+ missingArgument,
+ updateAngularProjects
+} from "../utils";
+import { Schema as ElementsOptions } from "./schema";
+import { getFileContent } from "@schematics/angular/utility/test";
+
+let customElementList: string;
+let componentSymbols: Array<{ symbol: string; selector: string }>;
+let componentSymbolList: string;
+let htmlElements: string;
+export default function(options: ElementsOptions) {
+ if (!options.builderModule) {
+ const example = `ng g elements menu --barrel=@mycompany/ui --components=menu,footer`;
+ if (!options.name) {
+ throw new SchematicsException(
+ missingArgument(
+ "name",
+ "Provide a name for the custom element module.",
+ example
+ )
+ );
+ }
+ if (!options.barrel) {
+ throw new SchematicsException(
+ missingArgument(
+ "barrel",
+ "Provide the name of the workspace barrel where your components live.",
+ example
+ )
+ );
+ }
+ if (!options.components) {
+ throw new SchematicsException(
+ missingArgument(
+ "components",
+ `Provide a comma delimited list of components you'd like to create as custom elements.`,
+ example
+ )
+ );
+ }
+ }
+
+ return chain([
+ prerun(options),
+ (tree: Tree) => {
+ if (!options.builderModule) {
+ const workspacePrefix = options.prefix || getPrefix() || "";
+ const htmlElementList = [];
+ componentSymbols = [];
+ // parse component names to standard convention
+ const componentNames = options.components.split(",");
+ for (let component of componentNames) {
+ // using short name ("menu" for a component named "MenuComponent")
+ // convert to fully best practice name
+ const isShortName =
+ component.toLowerCase().indexOf("component") === -1;
+ let selector = `${workspacePrefix ? `${workspacePrefix}-` : ""}`;
+ if (isShortName) {
+ selector += component.toLowerCase();
+ } else {
+ const parts = component.toLowerCase().split("component");
+ selector += parts[0];
+ }
+ componentSymbols.push({
+ selector,
+ symbol: `${stringUtils.classify(component)}${
+ isShortName ? "Component" : ""
+ }`
+ });
+ htmlElementList.push(`<${selector}>${selector}>`);
+ }
+ componentSymbolList = componentSymbols.map(c => c.symbol).join(", ");
+ htmlElements = htmlElementList.join("\n");
+
+ customElementList = createCustomElementList(componentSymbols);
+ }
+ return tree;
+ },
+ // add custom element module
+ (tree: Tree, context: SchematicContext) => {
+ return options.builderModule ? noop() : addFiles(options)(tree, context);
+ },
+ // add builder files or update them
+ (tree: Tree, context: SchematicContext) => {
+ if (tree.exists('xplat/web/elements/builder/index.html')) {
+ return updateBuilder(tree, options);
+ } else {
+ return addFiles(options, 'builder')(tree, context);
+ }
+ },
+ // adjust app files
+ // (tree: Tree) => adjustAppFiles(options, tree),
+ // add build scripts
+ (tree: Tree) => {
+ if (options.builderModule) {
+ return noop();
+ } else {
+ const scripts = {};
+ scripts[
+ `build.web.elements`
+ ] = `ng build web-elements --prod --output-hashing=none --single-bundle=true --keep-polyfills=true`;
+ scripts[`preview.web.elements`] = `http-server dist/ngelements`;
+ return updatePackageScripts(tree, scripts);
+ }
+ },
+ (tree: Tree) => {
+ if (options.builderModule) {
+ return noop();
+ } else {
+ const projects = {};
+ projects[`web-elements`] = {
+ root: "",
+ sourceRoot: "xplat/web/elements/builder",
+ projectType: "application",
+ prefix: "web-elements",
+ schematics: {},
+ architect: {
+ build: {
+ builder: "ngx-build-plus:build",
+ options: {
+ outputPath: "dist/ngelements",
+ index: "xplat/web/elements/builder/index.html",
+ main: "xplat/web/elements/builder/elements.ts",
+ polyfills: "xplat/web/elements/builder/polyfills.ts",
+ tsConfig: "xplat/web/elements/builder/tsconfig.elements.json"
+ },
+ configurations: {
+ production: {
+ optimization: true,
+ outputHashing: "all",
+ sourceMap: false,
+ extractCss: true,
+ namedChunks: false,
+ aot: true,
+ extractLicenses: true,
+ vendorChunk: false,
+ buildOptimizer: true
+ }
+ }
+ },
+ serve: {
+ builder: "ngx-build-plus:dev-server",
+ options: {
+ browserTarget: "web-elements:build"
+ },
+ configurations: {
+ production: {
+ browserTarget: "web-elements:build:production"
+ }
+ }
+ }
+ }
+ };
+ return updateAngularProjects(tree, projects);
+ }
+ },
+ // update dependencies
+ (tree: Tree, context: SchematicContext) => {
+ return options.builderModule ? noop() : updateWorkspaceSupport(options, tree, context);
+ },
+ // update for builderModule if desired
+ (tree: Tree, context: SchematicContext) => {
+ if (options.builderModule) {
+
+ } else {
+ return noop();
+ }
+ },
+ // formatting
+ options.skipFormat ? noop() : formatFiles(options)
+ ]);
+}
+
+function addFiles(options: ElementsOptions, extra: string = ""): Rule {
+ extra = extra ? `${extra}_` : "";
+ return branchAndMerge(
+ mergeWith(
+ apply(url(`./_${extra}files`), [
+ template({
+ ...(options as any),
+ name: options.name.toLowerCase(),
+ customElementList,
+ componentSymbolList,
+ componentSymbols,
+ htmlElements,
+ npmScope: getNpmScope(),
+ prefix: getPrefix(),
+ dot: ".",
+ utils: stringUtils
+ }),
+ move(`xplat/web/elements`)
+ ])
+ )
+ );
+}
+
+function updateWorkspaceSupport(
+ options: ElementsOptions,
+ tree: Tree,
+ context: SchematicContext
+) {
+ return updateJsonInTree("package.json", json => {
+ json.scripts = json.scripts || {};
+ json.dependencies = json.dependencies || {};
+ const angularVersion = json.dependencies["@angular/core"];
+ json.dependencies = {
+ ...json.dependencies,
+ "@angular/elements": angularVersion,
+ "@webcomponents/webcomponentsjs": "^2.2.7"
+ };
+ json.devDependencies = json.devDependencies || {};
+ json.devDependencies = {
+ ...json.devDependencies,
+ "http-server": "^0.11.1",
+ "ngx-build-plus": "^7.7.5"
+ };
+
+ return json;
+ })(tree, context);
+}
+
+function createCustomElementList(componentSymbols) {
+ const customElements = ["let component;"];
+ for (const comp of componentSymbols) {
+ customElements.push(`component = createCustomElement(${
+ comp.symbol
+ }, { injector: this.injector });
+ customElements.define('${comp.selector}', component);`);
+ }
+ return customElements.join("\n");
+}
+
+function updateBuilder(tree: Tree, options: ElementsOptions) {
+ if (options.builderModule) {
+ tree.overwrite(`xplat/web/elements/builder/elements.ts`, builderElementsContent(options.builderModule));
+ const moduleFilePath = `xplat/web/elements/${options.builderModule}.module.ts`;
+ if (tree.exists(moduleFilePath)) {
+ const moduleFile = getFileContent(tree, moduleFilePath);
+ const selectorParts = moduleFile.split('.define(');
+ selectorParts.splice(0,1); // remove starting data
+ const customElements = [];
+ for (const part of selectorParts) {
+ let selector = part.split(',')[0].replace(/'/ig, '').replace(/"/ig, '');
+ customElements.push(`<${selector}>${selector}>`);
+ }
+ tree.overwrite(`xplat/web/elements/builder/index.html`, buildIndexContent(customElements.join('\n')));
+ } else {
+ throw new SchematicsException(`${moduleFilePath} does not exist.`);
+ }
+ } else {
+ tree.overwrite(`xplat/web/elements/builder/elements.ts`, builderElementsContent(options.name));
+ tree.overwrite(`xplat/web/elements/builder/index.html`, buildIndexContent(htmlElements));
+ }
+ return tree;
+}
+
+function builderElementsContent(name: string) {
+ return `import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { ${stringUtils.classify(name)}Module } from '../${name}.module';
+
+platformBrowserDynamic()
+ .bootstrapModule(${stringUtils.classify(name)}Module)
+ .catch(err => console.log(err));
+`;
+}
+
+function buildIndexContent(customElements: string) {
+ return `
+
+
+
+ Custom Elements
+
+
+
+
+
+ ${customElements}
+
+`;
+}
diff --git a/src/elements/index_spec.ts b/src/elements/index_spec.ts
new file mode 100644
index 00000000..bf3c5ca8
--- /dev/null
+++ b/src/elements/index_spec.ts
@@ -0,0 +1,161 @@
+import { Tree, VirtualTree } from '@angular-devkit/schematics';
+import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
+import { getFileContent } from '@schematics/angular/utility/test';
+import * as path from 'path';
+
+import { Schema as ElementsOptions } from './schema';
+import { Schema as ComponentOptions } from '../component/schema';
+import { createXplatWithApps, isInModuleMetadata, createOrUpdate, createEmptyWorkspace } from '../utils';
+
+describe('elements schematic', () => {
+ const schematicRunner = new SchematicTestRunner(
+ '@nstudio/schematics',
+ path.join(__dirname, '../collection.json'),
+ );
+ const defaultOptions: ElementsOptions = {
+ name: 'ui-kit',
+ barrel: '@mycompany/web',
+ components: 'menu,footer'
+ };
+
+ let appTree: Tree;
+
+ beforeEach(() => {
+ appTree = new VirtualTree();
+ appTree = createEmptyWorkspace(appTree);
+ });
+
+ it('should create an elements module that provides the specified components', () => {
+ const options: ElementsOptions = { ...defaultOptions };
+ // console.log('appTree:', appTree);
+ let tree = schematicRunner.runSchematic('xplat', {
+ prefix: 'tt',
+ platforms: 'web'
+ }, appTree);
+ const componentOptions: ComponentOptions = {
+ name: 'menu',
+ platforms: 'web'
+ };
+ tree = schematicRunner.runSchematic('component', componentOptions, tree);
+ componentOptions.name = 'footer';
+ tree = schematicRunner.runSchematic('component', componentOptions, tree);
+ let files = tree.files;
+ // console.log(files.slice(85,files.length));
+ expect(files.indexOf('/xplat/web/features/ui/components/menu/menu.component.html')).toBeGreaterThanOrEqual(0);
+ expect(files.indexOf('/xplat/web/features/ui/components/menu/menu.component.ts')).toBeGreaterThanOrEqual(0);
+ expect(files.indexOf('/xplat/web/features/ui/components/footer/footer.component.html')).toBeGreaterThanOrEqual(0);
+ expect(files.indexOf('/xplat/web/features/ui/components/footer/footer.component.ts')).toBeGreaterThanOrEqual(0);
+
+ tree = schematicRunner.runSchematic('elements', options, tree);
+ files = tree.files;
+
+ const elementModulePath = '/xplat/web/elements/ui-kit.module.ts';
+ expect(files.indexOf(elementModulePath)).toBeGreaterThanOrEqual(0);
+ const elementModule = getFileContent(tree, elementModulePath);
+ // console.log(elementModule);
+ expect(elementModule.indexOf(`import { MenuComponent, FooterComponent } from '@mycompany/web';`)).toBeGreaterThanOrEqual(0);
+ expect(elementModule.indexOf(`createCustomElement(MenuComponent`)).toBeGreaterThanOrEqual(0);
+ expect(elementModule.indexOf(`define('tt-menu'`)).toBeGreaterThanOrEqual(0);
+ expect(elementModule.indexOf(`createCustomElement(FooterComponent`)).toBeGreaterThanOrEqual(0);
+ expect(elementModule.indexOf(`define('tt-footer'`)).toBeGreaterThanOrEqual(0);
+
+ const packageFile = getFileContent(tree, 'package.json');
+ // console.log(elementModule);
+ expect(packageFile.indexOf(`build.web.elements`)).toBeGreaterThanOrEqual(0);
+ expect(packageFile.indexOf(`preview.web.elements`)).toBeGreaterThanOrEqual(0);
+ });
+
+ it('--builderModule argument', () => {
+ const options: ElementsOptions = { ...defaultOptions };
+ // console.log('appTree:', appTree);
+ let tree = schematicRunner.runSchematic('xplat', {
+ prefix: 'tt',
+ platforms: 'web'
+ }, appTree);
+ const componentOptions: ComponentOptions = {
+ name: 'menu',
+ platforms: 'web'
+ };
+ tree = schematicRunner.runSchematic('component', componentOptions, tree);
+ componentOptions.name = 'footer';
+ tree = schematicRunner.runSchematic('component', componentOptions, tree);
+ let files = tree.files;
+ // console.log(files.slice(85,files.length));
+ expect(files.indexOf('/xplat/web/features/ui/components/menu/menu.component.html')).toBeGreaterThanOrEqual(0);
+ expect(files.indexOf('/xplat/web/features/ui/components/menu/menu.component.ts')).toBeGreaterThanOrEqual(0);
+ expect(files.indexOf('/xplat/web/features/ui/components/footer/footer.component.html')).toBeGreaterThanOrEqual(0);
+ expect(files.indexOf('/xplat/web/features/ui/components/footer/footer.component.ts')).toBeGreaterThanOrEqual(0);
+
+ tree = schematicRunner.runSchematic('elements', options, tree);
+ files = tree.files;
+
+ let elementModulePath = '/xplat/web/elements/ui-kit.module.ts';
+ expect(files.indexOf(elementModulePath)).toBeGreaterThanOrEqual(0);
+ let elementModule = getFileContent(tree, elementModulePath);
+ // console.log(elementModule);
+ expect(elementModule.indexOf(`import { MenuComponent, FooterComponent } from '@mycompany/web';`)).toBeGreaterThanOrEqual(0);
+
+ let builderPath = '/xplat/web/elements/builder/elements.ts';
+ expect(files.indexOf(builderPath)).toBeGreaterThanOrEqual(0);
+ let builderModule = getFileContent(tree, builderPath);
+ // console.log(builderModule);
+ expect(builderModule.indexOf(`../ui-kit.module`)).toBeGreaterThanOrEqual(0);
+ let builderIndexPath = '/xplat/web/elements/builder/index.html';
+ expect(files.indexOf(builderPath)).toBeGreaterThanOrEqual(0);
+ let builderIndex = getFileContent(tree, builderIndexPath);
+ // console.log(builderIndex);
+ expect(builderIndex.indexOf(` `)).toBeGreaterThanOrEqual(0);
+ expect(builderIndex.indexOf(` `)).toBeGreaterThanOrEqual(0);
+
+ const component2Options: ComponentOptions = {
+ name: 'dropdown',
+ platforms: 'web'
+ };
+ tree = schematicRunner.runSchematic('component', component2Options, tree);
+ component2Options.name = 'link';
+ tree = schematicRunner.runSchematic('component', component2Options, tree);
+ files = tree.files;
+ // console.log(files.slice(85,files.length));
+ expect(files.indexOf('/xplat/web/features/ui/components/dropdown/dropdown.component.html')).toBeGreaterThanOrEqual(0);
+ expect(files.indexOf('/xplat/web/features/ui/components/dropdown/dropdown.component.ts')).toBeGreaterThanOrEqual(0);
+ expect(files.indexOf('/xplat/web/features/ui/components/link/link.component.html')).toBeGreaterThanOrEqual(0);
+ expect(files.indexOf('/xplat/web/features/ui/components/link/link.component.ts')).toBeGreaterThanOrEqual(0);
+
+ const newElementOptions: ElementsOptions = {
+ name: 'widgets',
+ barrel: '@mycompany/web',
+ components: 'dropdown,link'
+ };
+ tree = schematicRunner.runSchematic('elements', newElementOptions, tree);
+ files = tree.files;
+
+ elementModulePath = '/xplat/web/elements/widgets.module.ts';
+ expect(files.indexOf(elementModulePath)).toBeGreaterThanOrEqual(0);
+ elementModule = getFileContent(tree, elementModulePath);
+ // console.log(elementModule);
+ expect(elementModule.indexOf(`import { DropdownComponent, LinkComponent } from '@mycompany/web';`)).toBeGreaterThanOrEqual(0);
+
+ builderModule = getFileContent(tree, builderPath);
+ // console.log(builderModule);
+ expect(builderModule.indexOf(`../widgets.module`)).toBeGreaterThanOrEqual(0);
+ builderIndex = getFileContent(tree, builderIndexPath);
+ // console.log(builderIndex);
+ expect(builderIndex.indexOf(` `)).toBeGreaterThanOrEqual(0);
+ expect(builderIndex.indexOf(` `)).toBeGreaterThanOrEqual(0);
+
+ const builderOption: ElementsOptions = {
+ builderModule: 'ui-kit',
+ };
+ tree = schematicRunner.runSchematic('elements', builderOption, tree);
+ files = tree.files;
+ builderModule = getFileContent(tree, builderPath);
+ // console.log(builderModule);
+ expect(builderModule.indexOf(`../ui-kit.module`)).toBeGreaterThanOrEqual(0);
+ builderIndex = getFileContent(tree, builderIndexPath);
+ // console.log(builderIndex);
+ expect(builderIndex.indexOf(` `)).toBeGreaterThanOrEqual(0);
+ expect(builderIndex.indexOf(` `)).toBeGreaterThanOrEqual(0);
+
+ });
+
+});
\ No newline at end of file
diff --git a/src/elements/schema.d.ts b/src/elements/schema.d.ts
new file mode 100644
index 00000000..9fe8fc8b
--- /dev/null
+++ b/src/elements/schema.d.ts
@@ -0,0 +1,24 @@
+export interface Schema {
+ name?: string;
+ /**
+ * The barrel in your workspace that contains the components you'd like to create as custom elements.
+ */
+ barrel?: string;
+ /**
+ * Comma delimited list of components from your barrel to create as custom elements.
+ */
+ components?: string;
+ /**
+ * Update builder files to use a different Angular Element module
+ */
+ builderModule?: string;
+ /**
+ * A unique prefix to add to each custom element. Defaults to workspace selector setting.
+ */
+ prefix?: string;
+ /**
+ * Skip formatting
+ */
+ skipFormat?: boolean;
+
+}
diff --git a/src/elements/schema.json b/src/elements/schema.json
new file mode 100644
index 00000000..43283700
--- /dev/null
+++ b/src/elements/schema.json
@@ -0,0 +1,38 @@
+{
+ "$schema": "http://json-schema.org/schema",
+ "id": "elements",
+ "title": "Create custom elements for the web.",
+ "type": "object",
+ "properties": {
+ "name": {
+ "description": "The name of the custom element module.",
+ "type": "string",
+ "$default": {
+ "$source": "argv",
+ "index": 0
+ }
+ },
+ "barrel": {
+ "description": "The barrel in your workspace that contains the components you'd like to create as custom elements.",
+ "type": "string"
+ },
+ "components": {
+ "description": "Comma delimited list of components from your barrel to create as custom elements.",
+ "type": "string"
+ },
+ "builderModule": {
+ "type": "string",
+ "description": "Update builder files to use a different Angular Element module. Used in isolation with no other options."
+ },
+ "prefix": {
+ "type": "string",
+ "description": "A unique prefix to add to each custom element. Defaults to workspace selector setting."
+ },
+ "skipFormat": {
+ "description": "Skip formatting files",
+ "type": "boolean",
+ "default": false
+ }
+ },
+ "required": []
+ }
\ No newline at end of file
diff --git a/src/feature/_ionic_component_files/components/__name__/__name__.component.ts b/src/feature/_ionic_component_files/components/__name__/__name__.component.ts
index 6d35e6b2..379c0d0c 100644
--- a/src/feature/_ionic_component_files/components/__name__/__name__.component.ts
+++ b/src/feature/_ionic_component_files/components/__name__/__name__.component.ts
@@ -1,13 +1,13 @@
import { Component } from '@angular/core';
-<% if (onlyProject || ignoreBase) { %>import { BaseComponent } from '@<%= npmScope %>/core';<%
+<% if (onlyProject || !createBase) { %>import { BaseComponent } from '@<%= npmScope %>/core';<%
} else { %>import { <%= utils.classify(name) %>BaseComponent } from '@<%= npmScope %>/features';<% } %>
@Component({
selector: '<%= prefix %>-<%= name %>',
templateUrl: '<%= name %>.component.html'
})
-export class <%= utils.classify(name) %>Component extends <%= onlyProject || ignoreBase ? '' : utils.classify(name) %>BaseComponent {
+export class <%= utils.classify(name) %>Component extends <%= onlyProject || !createBase ? '' : utils.classify(name) %>BaseComponent {
constructor() {
super();
diff --git a/src/feature/_lib_files/index.ts b/src/feature/_lib_files/index.ts
index 8e07ebfa..8f6969f1 100644
--- a/src/feature/_lib_files/index.ts
+++ b/src/feature/_lib_files/index.ts
@@ -1,2 +1,2 @@
-<% if (!onlyProject && !ignoreBase && !onlyModule) { %>export * from './base';<% } %>
+<% if (!onlyProject && createBase && !onlyModule) { %>export * from './base';<% } %>
export * from './<%= name %>.module';
diff --git a/src/feature/_nativescript_component_files/components/__name__/__name__.component.ts b/src/feature/_nativescript_component_files/components/__name__/__name__.component.ts
index 46c8ddea..26e37dd3 100644
--- a/src/feature/_nativescript_component_files/components/__name__/__name__.component.ts
+++ b/src/feature/_nativescript_component_files/components/__name__/__name__.component.ts
@@ -1,6 +1,6 @@
import { Component } from '@angular/core';
-<% if (onlyProject || ignoreBase) { %>import { BaseComponent } from '@<%= npmScope %>/core';<%
+<% if (onlyProject || !createBase) { %>import { BaseComponent } from '@<%= npmScope %>/core';<%
} else { %>import { <%= utils.classify(name) %>BaseComponent } from '@<%= npmScope %>/features';<% } %>
@Component({
@@ -8,7 +8,7 @@ import { Component } from '@angular/core';
selector: '<%= prefix %>-<%= name %>',
templateUrl: './<%= name %>.component.html'
})
-export class <%= utils.classify(name) %>Component extends <%= onlyProject || ignoreBase ? '' : utils.classify(name) %>BaseComponent {
+export class <%= utils.classify(name) %>Component extends <%= onlyProject || !createBase ? '' : utils.classify(name) %>BaseComponent {
constructor() {
super();
diff --git a/src/feature/_web_component_files/components/__name__/__name__.component.ts b/src/feature/_web_component_files/components/__name__/__name__.component.ts
index 6d35e6b2..379c0d0c 100644
--- a/src/feature/_web_component_files/components/__name__/__name__.component.ts
+++ b/src/feature/_web_component_files/components/__name__/__name__.component.ts
@@ -1,13 +1,13 @@
import { Component } from '@angular/core';
-<% if (onlyProject || ignoreBase) { %>import { BaseComponent } from '@<%= npmScope %>/core';<%
+<% if (onlyProject || !createBase) { %>import { BaseComponent } from '@<%= npmScope %>/core';<%
} else { %>import { <%= utils.classify(name) %>BaseComponent } from '@<%= npmScope %>/features';<% } %>
@Component({
selector: '<%= prefix %>-<%= name %>',
templateUrl: '<%= name %>.component.html'
})
-export class <%= utils.classify(name) %>Component extends <%= onlyProject || ignoreBase ? '' : utils.classify(name) %>BaseComponent {
+export class <%= utils.classify(name) %>Component extends <%= onlyProject || !createBase ? '' : utils.classify(name) %>BaseComponent {
constructor() {
super();
diff --git a/src/feature/index.ts b/src/feature/index.ts
index 9c444a26..708e6c94 100644
--- a/src/feature/index.ts
+++ b/src/feature/index.ts
@@ -164,7 +164,7 @@ export default function(options: featureOptions) {
: addFiles(options)(tree, context),
// libs
(tree: Tree, context: SchematicContext) =>
- options.onlyProject || options.ignoreBase || options.onlyModule
+ options.onlyProject || !options.createBase || options.onlyModule
? noop()(tree, context)
: addFiles(options, null, null, "_component")(tree, context),
// update libs index
diff --git a/src/feature/index_spec.ts b/src/feature/index_spec.ts
index 4bb23cbd..4c10fdd0 100644
--- a/src/feature/index_spec.ts
+++ b/src/feature/index_spec.ts
@@ -13,7 +13,8 @@ describe('feature schematic', () => {
);
const defaultOptions: FeatureOptions = {
name: 'foo',
- projects: 'nativescript-viewer,web-viewer'
+ projects: 'nativescript-viewer,web-viewer',
+ createBase: true
};
let appTree: Tree;
@@ -138,7 +139,7 @@ describe('feature schematic', () => {
expect(featureModule).toMatch(`import { UIModule } from \'../ui/ui.module\'`);
});
- it('should create feature module WITH a single starting component BUT IGNORE creating matching base component when using ignoreBase', () => {
+ it('should create feature module WITH a single starting component BUT IGNORE creating matching base component', () => {
// console.log('appTree:', appTree);
let tree = schematicRunner.runSchematic('xplat', {
prefix: 'tt',
@@ -150,8 +151,7 @@ describe('feature schematic', () => {
}, tree);
const options: FeatureOptions = {
name: 'foo',
- platforms: 'web',
- ignoreBase: true
+ platforms: 'web'
};
tree = schematicRunner.runSchematic('feature', options, tree);
const files = tree.files;
diff --git a/src/feature/schema.d.ts b/src/feature/schema.d.ts
index 0d3014f1..b8c48e70 100644
--- a/src/feature/schema.d.ts
+++ b/src/feature/schema.d.ts
@@ -17,9 +17,9 @@ export interface Schema {
*/
onlyModule?: boolean;
/**
- * Ignore base component generation
+ * Create base component for maximum code sharing
*/
- ignoreBase?: boolean;
+ createBase?: boolean;
/**
* Configure routing
*/
diff --git a/src/feature/schema.json b/src/feature/schema.json
index f333e6c8..f1dc6cb3 100644
--- a/src/feature/schema.json
+++ b/src/feature/schema.json
@@ -30,9 +30,9 @@
"description": "Generate just the module and ignore the default component.",
"default": false
},
- "ignoreBase": {
+ "createBase": {
"type": "boolean",
- "description": "Ignore base component generation.",
+ "description": "Create base component for maximum code sharing.",
"default": false
},
"routing": {
diff --git a/src/utils/errors.ts b/src/utils/errors.ts
index ab167f88..463c0758 100644
--- a/src/utils/errors.ts
+++ b/src/utils/errors.ts
@@ -20,8 +20,8 @@ export function helperMissingPlatforms() {
return `Missing platforms argument. Example: ng g xplat-helper imports --platforms=nativescript`;
}
-export function missingNameArgument(description: string = '', example: string = '') {
- return `Missing name argument. ${description} ${example ? 'Example: ' + example : ''}`;
+export function missingArgument(argName: string, description: string = '', example: string = '') {
+ return `Missing ${argName} argument. ${description} ${example ? 'Example: ' + example : ''}`;
}
export function noPlatformError() {
diff --git a/src/xplat-helper/index.ts b/src/xplat-helper/index.ts
index 4bae50ac..1c561a78 100644
--- a/src/xplat-helper/index.ts
+++ b/src/xplat-helper/index.ts
@@ -34,7 +34,7 @@ import {
unsupportedHelperError,
updateTsConfig,
helperTargetError,
- missingNameArgument
+ missingArgument,
} from "../utils";
import { Schema as HelperOptions } from "./schema";
// Helpers
@@ -94,7 +94,7 @@ let platforms: Array = [];
export default function(options: HelperOptions) {
if (!options.name) {
throw new SchematicsException(
- missingNameArgument('Provide a comma delimited list of helpers to generate.', 'ng g xplat-helper imports')
+ missingArgument('name', 'Provide a comma delimited list of helpers to generate.', 'ng g xplat-helper imports')
);
}
helpers = options.name.split(",");
diff --git a/src/xplat/_web_files/features/ui/components/header/header.component.html b/src/xplat/_web_files/features/ui/components/header/header.component.html
new file mode 100644
index 00000000..b0105d43
--- /dev/null
+++ b/src/xplat/_web_files/features/ui/components/header/header.component.html
@@ -0,0 +1,7 @@
+
+
Welcome to an Angular CLI app built with Nrwl Nx and xplat!
+
apps/{{ title }} in your workspace.
+
+
+
+
+
diff --git a/src/xplat/_web_files/features/ui/components/header/header.component.ts b/src/xplat/_web_files/features/ui/components/header/header.component.ts
new file mode 100644
index 00000000..7bf8f64b
--- /dev/null
+++ b/src/xplat/_web_files/features/ui/components/header/header.component.ts
@@ -0,0 +1,11 @@
+import { Component } from '@angular/core';
+
+import { HeaderBaseComponent } from '@<%= npmScope %>/features';
+
+@Component({
+ selector: '<%= prefix %>-header',
+ templateUrl: 'header.component.html'
+})
+export class HeaderComponent extends HeaderBaseComponent {
+
+}
diff --git a/src/xplat/_web_files/features/ui/components/index.ts b/src/xplat/_web_files/features/ui/components/index.ts
new file mode 100644
index 00000000..82a8b3c6
--- /dev/null
+++ b/src/xplat/_web_files/features/ui/components/index.ts
@@ -0,0 +1,7 @@
+import { HeaderComponent } from './header/header.component';
+
+export const UI_COMPONENTS = [
+ HeaderComponent,
+];
+
+export * from './header/header.component';
diff --git a/src/xplat/_web_files/features/ui/index.ts b/src/xplat/_web_files/features/ui/index.ts
index 516194eb..c65382f2 100644
--- a/src/xplat/_web_files/features/ui/index.ts
+++ b/src/xplat/_web_files/features/ui/index.ts
@@ -1 +1,2 @@
-export {UIModule} from './ui.module';
+export * from './components';
+export { UIModule } from './ui.module';
diff --git a/src/xplat/_web_files/features/ui/ui.module.ts b/src/xplat/_web_files/features/ui/ui.module.ts
index 90ba70fc..e654df75 100644
--- a/src/xplat/_web_files/features/ui/ui.module.ts
+++ b/src/xplat/_web_files/features/ui/ui.module.ts
@@ -5,6 +5,7 @@ import { RouterModule } from '@angular/router';
// libs
import { UISharedModule } from '@<%= npmScope %>/features';
+import { UI_COMPONENTS } from './components';
const MODULES = [
CommonModule,
@@ -18,8 +19,12 @@ const MODULES = [
imports: [
...MODULES
],
+ declarations: [
+ ...UI_COMPONENTS
+ ],
exports: [
- ...MODULES
+ ...MODULES,
+ ...UI_COMPONENTS
]
})
export class UIModule { }