chore: move create into separate repo

This commit is contained in:
Thomas Allmer
2020-10-18 15:50:41 +02:00
commit a9acd8a6f7
150 changed files with 10890 additions and 0 deletions

15
.eslintrc.js Normal file
View File

@@ -0,0 +1,15 @@
module.exports = {
extends: [require.resolve('@open-wc/eslint-config'), require.resolve('eslint-config-prettier')],
overrides: [
{
files: ['**/test/**/*.js', '**/*.config.js'],
rules: {
'no-console': 'off',
'no-unused-expressions': 'off',
'class-methods-use-this': 'off',
'max-classes-per-file': 'off',
'import/no-extraneous-dependencies': 'off', // we moved all devDependencies to root
},
},
],
};

29
.gitignore vendored Normal file
View File

@@ -0,0 +1,29 @@
## editors
/.idea
/.vscode
## system files
.DS_Store
## code coverage folders
coverage/
## npm
node_modules
npm-debug.log
yarn-error.log
## temp folders
/.tmp/
## we prefer yarn.lock
package-lock.json
## build output
dist
build-stats.json
stats.html
.rpt2_cache
## browserstack
local.log

1211
CHANGELOG.md Normal file

File diff suppressed because it is too large Load Diff

99
README.md Normal file
View File

@@ -0,0 +1,99 @@
---
permalink: 'init/index.html'
title: Create Open Web Components
section: guides
tags:
- guides
---
# Create Open Web Components
Web component project scaffolding.
[//]: # 'AUTO INSERT HEADER PREPUBLISH'
## Usage
```bash
npm init @open-wc
```
<div class="custom-block warning"><p class="custom-block-title">WARNING</p> <p><code>npm init</code> requires node 10 &amp; npm 6 or higher</p></div>
This will kickstart a menu guiding you through all available actions.
```
$ npm init @open-wc
npx: installed 14 in 4.074s
_.,,,,,,,,,._
.d'' ``b. Open Web Components Recommendations
.p' Open `q.
.d' Web Components `b. Start or upgrade your web component project with
.d' `b. ease. All our recommendations at your fingertips.
:: ................. ::
`p. .q' See more details at https://open-wc.org/init/
`p. open-wc.org .q'
`b. @openWc .d'
`q.. ..,' Note: you can exit any time with Ctrl+C or Esc
'',,,,,,,,,,''
? What would you like to do today? - Use arrow-keys. Return to submit.
Scaffold a new project
Upgrade an existing project
```
Our generators are very modular you can pick and choose as you see fit.
## Scaffold generators
These generators help you kickstart a new app or web component.
They will create a new folder and set up everything you need to get started immediately.
Example usage:
```bash
npm init @open-wc
# Select "Scaffold a new project"
```
### Available scaffold generators:
- `Web Component`<br/>
This generator scaffolds a starting point for a web component. We recommend using this generator when you want to develop and publish a single web component.
<br/>
- `Application`<br/>
This generator scaffolds a new starter application. We recommend using this generator at the start of your web component project.
<br/>
## Features
The above generators are the perfect playgrounds to prototype.
Add linting, testing, demoing and building whenever the need arises.
Example usage:
```bash
cd existing-web-component
npm init @open-wc
# select "Upgrade an existing project" or add features while scaffolding
```
### Available Upgrade features
- `Linting`<br>
This generator adds a complete linting setup with ESLint, Prettier, Husky and commitlint.
<br/>
- `Testing`<br>
This generator adds a complete testing setup with Web Test Runner.
<br/>
- `Demoing`<br>
This generator adds a complete demoing setup with Storybook.
<br/>
- `Building`<br>
This generator adds a complete building setup with rollup.
<br/>

16
babel.config.js Normal file
View File

@@ -0,0 +1,16 @@
module.exports = {
plugins: ['babel-plugin-transform-dynamic-import'],
ignore: ['./src/generators/*/templates/**/*'],
presets: [
[
'@babel/env',
{
targets: {
node: '10',
},
corejs: 2,
useBuiltIns: 'usage',
},
],
],
};

29
owc-app/.editorconfig Normal file
View File

@@ -0,0 +1,29 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# Change these settings to your own preference
indent_style = space
indent_size = 2
# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[*.json]
indent_size = 2
[*.{html,js,md}]
block_comment_start = /**
block_comment = *
block_comment_end = */

20
owc-app/.gitignore vendored Normal file
View File

@@ -0,0 +1,20 @@
## editors
/.idea
/.vscode
## system files
.DS_Store
## npm
/node_modules/
/npm-debug.log
## testing
/coverage/
## temp folders
/.tmp/
# build
/_site/
/dist/

21
owc-app/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 owc-app
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

30
owc-app/README.md Normal file
View File

@@ -0,0 +1,30 @@
<p align="center">
<img width="200" src="https://open-wc.org/hero.png"></img>
</p>
## Open-wc Starter App
[![Built with open-wc recommendations](https://img.shields.io/badge/built%20with-open--wc-blue.svg)](https://github.com/open-wc)
## Quickstart
To get started:
```bash
npm init @open-wc
# requires node 10 & npm 6 or higher
```
## Scripts
- `start` runs your app for development, reloading on file changes
- `start:build` runs your app after it has been built using the build command
- `build` builds your app and outputs it in your `dist` directory
- `test` runs your test suite with Web Test Runner
- `lint` runs the linter for your project
## Tooling configs
For most of the tools, the configuration is in the `package.json` to reduce the amount of files in your project.
If you customize the configuration a lot, you can consider moving them to individual files.

View File

@@ -0,0 +1,26 @@
{
"version": 2,
"tags": [
{
"name": "owc-app",
"description": "An application with a title and an action counter",
"properties": [
{
"name": "title",
"type": "String",
"description": "The title of your application",
"default": "Hey there"
},
{
"name": "page",
"type": "String",
"description": "Which page to show",
"default": "main"
}
],
"events": [],
"slots": [],
"cssProperties": []
}
]
}

28
owc-app/index.html Normal file
View File

@@ -0,0 +1,28 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<meta name="Description" content="Put your description here.">
<base href="/">
<style>
html,
body {
margin: 0;
padding: 0;
font-family: sans-serif;
background-color: #ededed;
}
</style>
<title>owc-app</title>
</head>
<body>
<owc-app></owc-app>
<script type="module" src="./src/owc-app.js"></script>
</body>
</html>

49
owc-app/package.json Normal file
View File

@@ -0,0 +1,49 @@
{
"name": "owc-app",
"version": "0.0.0",
"description": "Webcomponent owc-app following open-wc recommendations",
"license": "MIT",
"author": "owc-app",
"scripts": {
"format": "npm run format:eslint && npm run format:prettier",
"format:eslint": "eslint --ext .js,.html . --fix --ignore-path .gitignore",
"format:prettier": "prettier \"**/*.js\" --write --ignore-path .gitignore",
"lint": "npm run lint:eslint && npm run lint:prettier",
"lint:eslint": "eslint --ext .js,.html . --ignore-path .gitignore",
"lint:prettier": "prettier \"**/*.js\" --check --ignore-path .gitignore",
"start": "web-dev-server --app-index index.html --node-resolve --open --watch"
},
"dependencies": {
"lit-element": "^2.0.1",
"lit-html": "^1.0.0"
},
"devDependencies": {
"@open-wc/eslint-config": "^2.0.0",
"@web/dev-server": "^0.0.12",
"husky": "^1.0.0",
"lint-staged": "^10.0.0",
"prettier": "^2.0.4"
},
"eslintConfig": {
"extends": [
"@open-wc/eslint-config",
"eslint-config-prettier"
]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.js": [
"eslint --fix",
"prettier --write",
"git add"
]
},
"prettier": {
"singleQuote": true,
"arrowParens": "avoid"
}
}

84
owc-app/src/OwcApp.js Normal file
View File

@@ -0,0 +1,84 @@
import { LitElement, html, css } from 'lit-element';
import { openWcLogo } from './open-wc-logo.js';
export class OwcApp extends LitElement {
static get properties() {
return {
title: { type: String },
page: { type: String },
};
}
static get styles() {
return css`
:host {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
font-size: calc(10px + 2vmin);
color: #1a2b42;
max-width: 960px;
margin: 0 auto;
text-align: center;
}
main {
flex-grow: 1;
}
.logo > svg {
margin-top: 36px;
animation: app-logo-spin infinite 20s linear;
}
@keyframes app-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.app-footer {
font-size: calc(12px + 0.5vmin);
align-items: center;
}
.app-footer a {
margin-left: 5px;
}
`;
}
render() {
return html`
<main>
<div class="logo">${openWcLogo}</div>
<h1>My app</h1>
<p>Edit <code>src/OwcApp.js</code> and save to reload.</p>
<a
class="app-link"
href="https://open-wc.org/developing/#code-examples"
target="_blank"
rel="noopener noreferrer"
>
Code examples
</a>
</main>
<p class="app-footer">
🚽 Made with love by
<a
target="_blank"
rel="noopener noreferrer"
href="https://github.com/open-wc"
>open-wc</a
>.
</p>
`;
}
}

View File

@@ -0,0 +1,33 @@
import { html } from 'lit-html';
export const openWcLogo = html`
<svg
width="244px"
height="244px"
viewBox="0 0 244 244"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
<stop stop-color="#9B00FF" offset="0%"></stop>
<stop stop-color="#0077FF" offset="100%"></stop>
</linearGradient>
</defs>
<g
id="Page-1"
stroke="none"
stroke-width="1"
fill="none"
fill-rule="evenodd"
>
<path
d="M205.639259,176.936244 C207.430887,174.217233 209.093339,171.405629 210.617884,168.510161 M215.112174,158.724316 C216.385153,155.50304 217.495621,152.199852 218.433474,148.824851 M220.655293,138.874185 C221.231935,135.482212 221.637704,132.03207 221.863435,128.532919 M222,118.131039 C221.860539,114.466419 221.523806,110.85231 221.000113,107.299021 M218.885321,96.8583653 C218.001583,93.4468963 216.942225,90.1061026 215.717466,86.8461994 M211.549484,77.3039459 C209.957339,74.1238901 208.200597,71.0404957 206.290425,68.0649233 M200.180513,59.5598295 C181.848457,36.6639805 153.655709,22 122.036748,22 C66.7879774,22 22,66.771525 22,122 C22,177.228475 66.7879774,222 122.036748,222 C152.914668,222 180.52509,208.015313 198.875424,186.036326"
id="Shape"
stroke="url(#linearGradient-1)"
stroke-width="42.0804674"
></path>
</g>
</svg>
`;

3
owc-app/src/owc-app.js Normal file
View File

@@ -0,0 +1,3 @@
import { OwcApp } from './OwcApp.js';
customElements.define('owc-app', OwcApp);

61
package.json Normal file
View File

@@ -0,0 +1,61 @@
{
"name": "@open-wc/create",
"version": "0.36.0",
"publishConfig": {
"access": "public"
},
"description": "Easily setup all the tools of Open Web Components.",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/open-wc/open-wc.git",
"directory": "packages/create"
},
"author": "open-wc",
"homepage": "https://github.com/open-wc/open-wc/tree/master/packages/create",
"bin": {
"create-open-wc": "./dist/create.js"
},
"scripts": {
"build": "rm -rf dist && babel src --out-dir dist --copy-files --include-dotfiles",
"prepublishOnly": "npm run build && ../../scripts/insert-header.js",
"start": "npm run build && node ./dist/create.js",
"test": "npm run test:node",
"test:node": "mocha --require @babel/register",
"test:update-snapshots": "node -r @babel/register ./test/update-snapshots.js",
"test:watch": "onchange 'src/**/*.js' 'test/**/*.js' -- npm run test --silent"
},
"files": [
"dist"
],
"keywords": [
"open-wc",
"owc",
"generator",
"starter-app"
],
"dependencies": {
"chalk": "^2.4.2",
"command-line-args": "^5.0.2",
"dedent": "^0.7.0",
"deepmerge": "^4.2.2",
"glob": "^7.1.3",
"prompts": "^2.1.0",
"semver": "^6.1.0"
},
"devDependencies": {
"@babel/cli": "^7.8.4",
"@babel/core": "^7.11.1",
"@babel/preset-env": "^7.9.0",
"@babel/register": "^7.9.0",
"@open-wc/building-rollup": "^1.9.3",
"@open-wc/eslint-config": "^3.0.0",
"babel-plugin-transform-dynamic-import": "^2.1.0",
"chai": "^4.2.0",
"chai-fs": "^2.0.0",
"eslint": "^7.11.0",
"eslint-config-prettier": "^6.13.0",
"mocha": "^6.2.2",
"onchange": "^5.2.0"
}
}

121
src/Generator.js Normal file
View File

@@ -0,0 +1,121 @@
/* eslint-disable no-console, import/no-cycle */
import prompts from 'prompts';
import path from 'path';
import {
copyTemplates,
copyTemplate,
copyTemplateJsonInto,
installNpm,
writeFilesToDisk,
optionsToCommand,
} from './core.js';
/**
* Options for the generator
* @typedef {object} GeneratorOptions
* @property {string} [tagName] the dash-case tag name
* @property {string} [destinationPath='auto'] path to output to. default value 'auto' will output to current working directory
* @property {'scaffold'} [type='scaffold'] path to output to. default value 'auto' will output to current working directory
* @property {'true'|'false'} [writeToDisk] whether to write to disk
* @property {'yarn'|'npm'|'false'} [installDependencies] whether and with which tool to install dependencies
*/
/**
* dash-case to PascalCase
* @param {string} tagName dash-case tag name
* @return {string} PascalCase class name
*/
function getClassName(tagName) {
return tagName
.split('-')
.reduce((previous, part) => previous + part.charAt(0).toUpperCase() + part.slice(1), '');
}
class Generator {
constructor() {
/**
* @type {GeneratorOptions}
*/
this.options = {
destinationPath: 'auto',
};
this.templateData = {};
this.wantsNpmInstall = true;
this.wantsWriteToDisk = true;
this.wantsRecreateInfo = true;
this.generatorName = '@open-wc';
}
execute() {
if (this.options.tagName) {
const { tagName } = this.options;
const className = getClassName(tagName);
this.templateData = { ...this.templateData, tagName, className };
if (this.options.destinationPath === 'auto') {
this.options.destinationPath = process.cwd();
if (this.options.type === 'scaffold') {
this.options.destinationPath = path.join(process.cwd(), tagName);
}
}
}
}
destinationPath(destination = '') {
return path.join(this.options.destinationPath, destination);
}
copyTemplate(from, to) {
copyTemplate(from, to, this.templateData);
}
copyTemplateJsonInto(from, to, options = { mode: 'merge' }) {
copyTemplateJsonInto(from, to, this.templateData, options);
}
async copyTemplates(from, to = this.destinationPath()) {
return copyTemplates(from, to, this.templateData);
}
async end() {
if (this.wantsWriteToDisk) {
this.options.writeToDisk = await writeFilesToDisk();
}
if (this.wantsNpmInstall) {
const answers = await prompts(
[
{
type: 'select',
name: 'installDependencies',
message: 'Do you want to install dependencies?',
choices: [
{ title: 'No', value: 'false' },
{ title: 'Yes, with yarn', value: 'yarn' },
{ title: 'Yes, with npm', value: 'npm' },
],
},
],
{
onCancel: () => {
process.exit();
},
},
);
this.options.installDependencies = answers.installDependencies;
const { installDependencies } = this.options;
if (installDependencies === 'yarn' || installDependencies === 'npm') {
await installNpm(this.options.destinationPath, installDependencies);
}
}
if (this.wantsRecreateInfo) {
console.log('');
console.log('If you want to rerun this exact same generator you can do so by executing:');
console.log(optionsToCommand(this.options, this.generatorName));
}
}
}
export default Generator;

430
src/core.js Normal file
View File

@@ -0,0 +1,430 @@
/* eslint-disable no-console, import/no-cycle */
import { spawn } from 'child_process';
import deepmerge from 'deepmerge';
import fs from 'fs';
import glob from 'glob';
import path from 'path';
import prompts from 'prompts';
import Generator from './Generator.js';
/**
*
* @param {Function[]} mixins
* @param {typeof Generator} Base
*/
export async function executeMixinGenerator(mixins, options = {}, Base = Generator) {
class Start extends Base {}
mixins.forEach(mixin => {
// @ts-ignore
// eslint-disable-next-line no-class-assign
Start = mixin(Start);
});
// class Do extends mixins(Base) {}
const inst = new Start();
inst.options = { ...inst.options, ...options };
await inst.execute();
if (!options.noEnd) {
await inst.end();
}
}
export const virtualFiles = [];
export function resetVirtualFiles() {
virtualFiles.length = 0;
}
/**
* Minimal template system.
* Replaces <%= name %> if provides as template
*
* @example
* processTemplate('prefix <%= name %> suffix', { name: 'foo' })
* // prefix foo suffix
*
* @param {string} _fileContent Template as a string
* @param {object} data Object of all the variables to repalce
* @returns {string} Template with all replacements
*/
export function processTemplate(_fileContent, data = {}) {
let fileContent = _fileContent;
Object.keys(data).forEach(key => {
fileContent = fileContent.replace(new RegExp(`<%= ${key} %>`, 'g'), data[key]);
});
return fileContent;
}
/**
* Minimal virtual file system
* Stores files to write in an array
*
* @param {string} filePath
* @param {string} content
*/
export function writeFileToPath(filePath, content) {
let addNewFile = true;
virtualFiles.forEach((fileMeta, index) => {
if (fileMeta.path === filePath) {
virtualFiles[index].content = content;
addNewFile = false;
}
});
if (addNewFile === true) {
virtualFiles.push({ path: filePath, content });
}
}
/**
*
* @param {string} filePath
*/
export function readFileFromPath(filePath) {
let content = false;
virtualFiles.forEach((fileMeta, index) => {
if (fileMeta.path === filePath) {
// eslint-disable-next-line prefer-destructuring
content = virtualFiles[index].content;
}
});
if (content) {
return content;
}
if (fs.existsSync(filePath)) {
return fs.readFileSync(filePath, 'utf-8');
}
return false;
}
/**
*
* @param {string} filePath
*/
export function deleteVirtualFile(filePath) {
const index = virtualFiles.findIndex(fileMeta => fileMeta.path === filePath);
if (index !== -1) {
virtualFiles.splice(index, 1);
}
}
let overwriteAllFiles = false;
/**
*
* @param {boolean} value
*/
export function setOverrideAllFiles(value) {
overwriteAllFiles = value;
}
/**
*
* @param {string} toPath
* @param {string} fileContent
* @param {object} obj Options
*/
export async function writeFileToPathOnDisk(
toPath,
fileContent,
{ override = false, ask = true } = {},
) {
const toPathDir = path.dirname(toPath);
if (!fs.existsSync(toPathDir)) {
fs.mkdirSync(toPathDir, { recursive: true });
}
if (fs.existsSync(toPath)) {
if (override || overwriteAllFiles) {
fs.writeFileSync(toPath, fileContent);
} else if (ask) {
let wantOverride = overwriteAllFiles;
if (!wantOverride) {
const answers = await prompts(
[
{
type: 'select',
name: 'overwriteFile',
message: `Do you want to overwrite ${toPath}?`,
choices: [
{ title: 'Yes', value: 'true' },
{
title: 'Yes for all files',
value: 'always',
},
{ title: 'No', value: 'false' },
],
},
],
{
onCancel: () => {
process.exit();
},
},
);
if (answers.overwriteFile === 'always') {
setOverrideAllFiles(true);
wantOverride = true;
}
if (answers.overwriteFile === 'true') {
wantOverride = true;
}
}
if (wantOverride) {
fs.writeFileSync(toPath, fileContent);
}
}
} else {
fs.writeFileSync(toPath, fileContent);
}
}
/**
* @param {String[]} allFiles pathes to files
* @param {Number} [level] internal to track nesting level
*/
export function filesToTree(allFiles, level = 0) {
const files = allFiles.filter(file => !file.includes('/'));
const dirFiles = allFiles.filter(file => file.includes('/'));
let indent = '';
for (let i = 1; i < level; i += 1) {
indent += '│ ';
}
let output = '';
const processed = [];
if (dirFiles.length > 0) {
dirFiles.forEach(dirFile => {
if (!processed.includes(dirFile)) {
const dir = `${dirFile.split('/').shift()}/`;
const subFiles = [];
allFiles.forEach(file => {
if (file.startsWith(dir)) {
subFiles.push(file.substr(dir.length));
processed.push(file);
}
});
output += level === 0 ? `${dir}\n` : `${indent}├── ${dir}\n`;
output += filesToTree(subFiles, level + 1);
}
});
}
if (files.length === 1) {
output += `${indent}└── ${files[0]}\n`;
}
if (files.length > 1) {
const last = files.pop();
output += `${indent}├── `;
output += files.join(`\n${indent}├── `);
output += `\n${indent}└── ${last}\n`;
}
return output;
}
/**
*
*/
export async function writeFilesToDisk() {
const treeFiles = [];
const root = process.cwd();
virtualFiles.sort((a, b) => {
const pathA = a.path.toLowerCase();
const pathB = b.path.toLowerCase();
if (pathA < pathB) return -1;
if (pathA > pathB) return 1;
return 0;
});
virtualFiles.forEach(vFile => {
if (vFile.path.startsWith(root)) {
let vFilePath = './';
vFilePath += vFile.path.substr(root.length + 1);
treeFiles.push(vFilePath);
}
});
console.log('');
console.log(filesToTree(treeFiles));
const answers = await prompts(
[
{
type: 'select',
name: 'writeToDisk',
message: 'Do you want to write this file structure to disk?',
choices: [
{ title: 'Yes', value: 'true' },
{ title: 'No', value: 'false' },
],
},
],
{
onCancel: () => {
process.exit();
},
},
);
if (answers.writeToDisk === 'true') {
// eslint-disable-next-line no-restricted-syntax
for (const fileMeta of virtualFiles) {
// eslint-disable-next-line no-await-in-loop
await writeFileToPathOnDisk(fileMeta.path, fileMeta.content);
}
console.log('Writing..... done');
}
return answers.writeToDisk;
}
export function optionsToCommand(options, generatorName = '@open-wc') {
let command = `npm init ${generatorName} `;
Object.keys(options).forEach(key => {
const value = options[key];
if (typeof value === 'string' || typeof value === 'number') {
command += `--${key} ${value} `;
} else if (typeof value === 'boolean' && value === true) {
command += `--${key} `;
} else if (Array.isArray(value)) {
command += `--${key} ${value.join(' ')} `;
}
});
return command;
}
/**
*
* @param {string} fromPath
* @param {string} toPath
* @param {object} data
*/
export function copyTemplate(fromPath, toPath, data) {
const fileContent = readFileFromPath(fromPath);
if (fileContent) {
const processed = processTemplate(fileContent, data);
writeFileToPath(toPath, processed);
}
}
/**
*
* @param {string} fromGlob
* @param {string} [toDir] Directory to copy into
* @param {object} data Replace parameters in files
*/
export function copyTemplates(fromGlob, toDir = process.cwd(), data = {}) {
return new Promise(resolve => {
glob(fromGlob, { dot: true }, (er, files) => {
const copiedFiles = [];
files.forEach(filePath => {
if (!fs.lstatSync(filePath).isDirectory()) {
const fileContent = readFileFromPath(filePath);
if (fileContent !== false) {
const processed = processTemplate(fileContent, data);
// find path write to (force / also on windows)
const replace = path.join(fromGlob.replace(/\*/g, '')).replace(/\\(?! )/g, '/');
const toPath = filePath.replace(replace, `${toDir}/`);
copiedFiles.push({ toPath, processed });
writeFileToPath(toPath, processed);
}
}
});
resolve(copiedFiles);
});
});
}
/**
*
* @param {string} fromPath
* @param {string} toPath
* @param {object} data
*/
export function copyTemplateJsonInto(
fromPath,
toPath,
data = {},
{ mode = 'merge' } = { mode: 'merge' },
) {
const content = readFileFromPath(fromPath);
if (content === false) {
return;
}
const processed = processTemplate(content, data);
const mergeMeObj = JSON.parse(processed);
const overwriteMerge = (destinationArray, sourceArray) => sourceArray;
const emptyTarget = value => (Array.isArray(value) ? [] : {});
const clone = (value, options) => deepmerge(emptyTarget(value), value, options);
const combineMerge = (target, source, options) => {
const destination = target.slice();
source.forEach((item, index) => {
if (typeof destination[index] === 'undefined') {
const cloneRequested = options.clone !== false;
const shouldClone = cloneRequested && options.isMergeableObject(item);
destination[index] = shouldClone ? clone(item, options) : item;
} else if (options.isMergeableObject(item)) {
destination[index] = deepmerge(target[index], item, options);
} else if (target.indexOf(item) === -1) {
destination.push(item);
}
});
return destination;
};
const mergeOptions = { arrayMerge: combineMerge };
if (mode === 'override') {
mergeOptions.arrayMerge = overwriteMerge;
}
let finalObj = mergeMeObj;
const sourceContent = readFileFromPath(toPath);
if (sourceContent) {
finalObj = deepmerge(JSON.parse(sourceContent), finalObj, mergeOptions);
}
writeFileToPath(toPath, JSON.stringify(finalObj, null, 2));
}
/**
* @param {string} command
* @param {object} options
*/
function _install(command = 'npm', options) {
return new Promise(resolve => {
const install = spawn(command, ['install'], options);
install.stdout.on('data', data => {
console.log(`${data}`.trim());
});
install.stderr.on('data', data => {
console.log(`${command}: ${data}`);
});
install.on('close', () => {
resolve();
});
});
}
/**
*
* @param {string} where
* @param {string} command
*/
export async function installNpm(where, command) {
console.log('');
console.log('Installing dependencies...');
console.log('This might take some time...');
console.log(`Using ${command} to install...`);
await _install(command, { cwd: where, shell: true });
console.log('');
}

26
src/create.js Normal file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/env node
/* eslint-disable no-console */
import semver from 'semver';
import chalk from 'chalk';
import { executeMixinGenerator } from './core.js';
import { AppMixin } from './generators/app/index.js';
(async () => {
try {
if (semver.lte(process.version, '10.12.0')) {
console.log(chalk.bgRed('\nUh oh! Looks like you dont have Node v10.12.0 installed!\n'));
console.log(`You can do this by going to ${chalk.underline.blue(`https://nodejs.org/`)}
Or if you use nvm:
$ nvm install node ${chalk.gray(`# "node" is an alias for the latest version`)}
$ nvm use node
`);
} else {
await executeMixinGenerator([AppMixin]);
}
} catch (err) {
console.log(err);
}
})();

View File

@@ -0,0 +1,81 @@
import { CommonRepoMixin } from '../common-repo/index.js';
/* eslint-disable no-console */
export const TsAppLitElementMixin = subclass =>
class extends CommonRepoMixin(subclass) {
async execute() {
await super.execute();
const { tagName, className } = this.templateData;
// write & rename el class template
this.copyTemplate(
`${__dirname}/templates/_my-app.ts`,
this.destinationPath(`src//${tagName}.ts`),
);
this.copyTemplate(
`${__dirname}/templates/_MyApp.ts`,
this.destinationPath(`src/${className}.ts`),
);
this.copyTemplate(
`${__dirname}/templates/_open-wc-logo.ts`,
this.destinationPath(`src/open-wc-logo.ts`),
);
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
this.copyTemplate(
`${__dirname}/templates/_tsconfig.json`,
this.destinationPath('tsconfig.json'),
);
await this.copyTemplates(`${__dirname}/templates/static/**/*`);
this.copyTemplate(
`${__dirname}/templates/custom-elements.json`,
this.destinationPath('custom-elements.json'),
);
if (this.options.features && this.options.features.includes('testing')) {
await this.copyTemplates(`${__dirname}/templates/static-testing/**/*`);
}
if (this.options.features && this.options.features.includes('demoing')) {
await this.copyTemplates(`${__dirname}/templates/static-demoing/**/*`);
}
if (this.options.scaffoldFilesFor && this.options.scaffoldFilesFor.includes('demoing')) {
this.copyTemplate(
`${__dirname}/templates/_my-app.stories.ts`,
this.destinationPath(`./stories/${tagName}.stories.ts`),
);
await this.copyTemplates(`${__dirname}/templates/static-scaffold-demoing/**/*`);
}
if (this.options.scaffoldFilesFor && this.options.scaffoldFilesFor.includes('testing')) {
this.copyTemplate(
`${__dirname}/templates/_my-app.test.ts`,
this.destinationPath(`./test/${tagName}.test.ts`),
);
await this.copyTemplates(`${__dirname}/templates/static-scaffold-testing/**/*`);
}
}
async end() {
await super.end();
console.log('');
console.log('You are all set up now!');
console.log('');
console.log('All you need to do is run:');
console.log(` cd ${this.templateData.tagName}`);
console.log(' npm run start');
console.log('');
}
};

View File

@@ -0,0 +1,80 @@
import { LitElement, html, css, property } from 'lit-element';
import { openWcLogo } from './open-wc-logo.js';
export class <%= className %> extends LitElement {
@property({type: String}) page = 'main';
@property({type: String}) title = '';
static styles = css`
:host {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
font-size: calc(10px + 2vmin);
color: #1a2b42;
max-width: 960px;
margin: 0 auto;
text-align: center;
}
main {
flex-grow: 1;
}
.logo > svg {
margin-top: 36px;
animation: app-logo-spin infinite 20s linear;
}
@keyframes app-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.app-footer {
font-size: calc(12px + 0.5vmin);
align-items: center;
}
.app-footer a {
margin-left: 5px;
}
`;
render() {
return html`
<main>
<div class="logo">${openWcLogo}</div>
<h1>My app</h1>
<p>Edit <code>src/<%= className %>.js</code> and save to reload.</p>
<a
class="app-link"
href="https://open-wc.org/developing/#code-examples"
target="_blank"
rel="noopener noreferrer"
>
Code examples
</a>
</main>
<p class="app-footer">
🚽 Made with love by
<a
target="_blank"
rel="noopener noreferrer"
href="https://github.com/open-wc"
>open-wc</a
>.
</p>
`;
}
}

View File

@@ -0,0 +1,11 @@
import { html } from 'lit-html';
import '../src/<%= tagName %>.js';
export default {
title: '<%= tagName %>',
};
export const App = () =>
html`
<<%= tagName %>></<%= tagName %>>
`;

View File

@@ -0,0 +1,23 @@
import { html, fixture, expect } from '@open-wc/testing';
import {<%= className %>} from '../src/<%= className %>.js';
import '../src/<%= tagName %>.js';
describe('<%= className %>', () => {
let element: <%= className %>;
beforeEach(async () => {
element = await fixture(html`
<<%= tagName %>></<%= tagName %>>
`);
});
it('renders a h1', () => {
const h1 = element.shadowRoot!.querySelector('h1')!;
expect(h1).to.exist;
expect(h1.textContent).to.equal('My app');
});
it('passes the a11y audit', async () => {
await expect(element).shadowDom.to.be.accessible();
});
});

View File

@@ -0,0 +1,3 @@
import { <%= className %> } from './<%= className %>.js';
customElements.define('<%= tagName %>', <%= className %>);

View File

@@ -0,0 +1,33 @@
import { html } from 'lit-html';
export const openWcLogo = html`
<svg
width="244px"
height="244px"
viewBox="0 0 244 244"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
<stop stop-color="#9B00FF" offset="0%"></stop>
<stop stop-color="#0077FF" offset="100%"></stop>
</linearGradient>
</defs>
<g
id="Page-1"
stroke="none"
stroke-width="1"
fill="none"
fill-rule="evenodd"
>
<path
d="M205.639259,176.936244 C207.430887,174.217233 209.093339,171.405629 210.617884,168.510161 M215.112174,158.724316 C216.385153,155.50304 217.495621,152.199852 218.433474,148.824851 M220.655293,138.874185 C221.231935,135.482212 221.637704,132.03207 221.863435,128.532919 M222,118.131039 C221.860539,114.466419 221.523806,110.85231 221.000113,107.299021 M218.885321,96.8583653 C218.001583,93.4468963 216.942225,90.1061026 215.717466,86.8461994 M211.549484,77.3039459 C209.957339,74.1238901 208.200597,71.0404957 206.290425,68.0649233 M200.180513,59.5598295 C181.848457,36.6639805 153.655709,22 122.036748,22 C66.7879774,22 22,66.771525 22,122 C22,177.228475 66.7879774,222 122.036748,222 C152.914668,222 180.52509,208.015313 198.875424,186.036326"
id="Shape"
stroke="url(#linearGradient-1)"
stroke-width="42.0804674"
></path>
</g>
</svg>
`;

View File

@@ -0,0 +1,19 @@
{
"name": "<%= tagName %>",
"license": "MIT",
"scripts": {
"start": "concurrently --kill-others --names tsc,web-dev-server \"npm run tsc:watch\" \"web-dev-server --app-index index.html --node-resolve --open --watch\"",
"tsc:watch": "tsc --watch"
},
"dependencies": {
"lit-html": "^1.0.0",
"lit-element": "^2.0.1"
},
"devDependencies": {
"@types/node": "13.11.1",
"@web/dev-server": "^0.0.12",
"typescript": "~4.0.3",
"concurrently": "^5.1.0",
"tslib": "^1.11.0"
}
}

View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es2018",
"module": "esnext",
"moduleResolution": "node",
"noEmitOnError": true,
"lib": ["es2017", "dom"],
"strict": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"importHelpers": true,
"outDir": "out-tsc",
"sourceMap": true,
"inlineSources": true,
"rootDir": "./"
},
"include": ["**/*.ts"]
}

View File

@@ -0,0 +1,26 @@
{
"version": 2,
"tags": [
{
"name": "<%= tagName %>",
"description": "An application with a title and an action counter",
"properties": [
{
"name": "title",
"type": "String",
"description": "The title of your application",
"default": "Hey there"
},
{
"name": "page",
"type": "String",
"description": "Which page to show",
"default": "main"
}
],
"events": [],
"slots": [],
"cssProperties": []
}
]
}

View File

@@ -0,0 +1,14 @@
module.exports = {
stories: ['../**/stories/*.stories.{js,md,mdx}'],
addons: [
'storybook-prebuilt/addon-knobs/register.js',
'storybook-prebuilt/addon-docs/register.js',
'storybook-prebuilt/addon-viewport/register.js',
],
esDevServer: {
// custom es-dev-server options
nodeResolve: true,
watch: true,
open: true
},
};

View File

@@ -0,0 +1,17 @@
import { addParameters, setCustomElements } from '@open-wc/demoing-storybook';
addParameters({
docs: {
iframeHeight: '200px',
}
});
async function run() {
const customElements = await (
await fetch(new URL('../custom-elements.json', import.meta.url))
).json();
setCustomElements(customElements);
}
run();

View File

@@ -0,0 +1,4 @@
export default {
files: 'out-tsc/test/**/*.test.js',
nodeResolve: true
};

View File

@@ -0,0 +1,30 @@
<p align="center">
<img width="200" src="https://open-wc.org/hero.png"></img>
</p>
## Open-wc Starter App
[![Built with open-wc recommendations](https://img.shields.io/badge/built%20with-open--wc-blue.svg)](https://github.com/open-wc)
## Quickstart
To get started:
```sh
npm init @open-wc
# requires node 10 & npm 6 or higher
```
## Scripts
- `start` runs your app for development, reloading on file changes
- `start:build` runs your app after it has been built using the build command
- `build` builds your app and outputs it in your `dist` directory
- `test` runs your test suite with Web Test Runner
- `lint` runs the linter for your project
## Tooling configs
For most of the tools, the configuration is in the `package.json` to reduce the amount of files in your project.
If you customize the configuration a lot, you can consider moving them to individual files.

View File

@@ -0,0 +1,28 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<meta name="Description" content="Put your description here.">
<base href="/">
<style>
html,
body {
margin: 0;
padding: 0;
font-family: sans-serif;
background-color: #ededed;
}
</style>
<title><%= tagName %></title>
</head>
<body>
<<%= tagName %>></<%= tagName %>>
<script type="module" src="./out-tsc/src/<%= tagName %>.js"></script>
</body>
</html>

View File

@@ -0,0 +1,76 @@
import { CommonRepoMixin } from '../common-repo/index.js';
/* eslint-disable no-console */
export const AppLitElementMixin = subclass =>
class extends CommonRepoMixin(subclass) {
async execute() {
await super.execute();
const { tagName, className } = this.templateData;
// write & rename el class template
this.copyTemplate(
`${__dirname}/templates/_my-app.js`,
this.destinationPath(`src//${tagName}.js`),
);
this.copyTemplate(
`${__dirname}/templates/_MyApp.js`,
this.destinationPath(`src/${className}.js`),
);
this.copyTemplate(
`${__dirname}/templates/_open-wc-logo.js`,
this.destinationPath(`src/open-wc-logo.js`),
);
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
await this.copyTemplates(`${__dirname}/templates/static/**/*`);
this.copyTemplate(
`${__dirname}/templates/custom-elements.json`,
this.destinationPath('custom-elements.json'),
);
if (this.options.features && this.options.features.includes('testing')) {
await this.copyTemplates(`${__dirname}/templates/static-testing/**/*`);
}
if (this.options.features && this.options.features.includes('demoing')) {
await this.copyTemplates(`${__dirname}/templates/static-demoing/**/*`);
}
if (this.options.scaffoldFilesFor && this.options.scaffoldFilesFor.includes('demoing')) {
this.copyTemplate(
`${__dirname}/templates/_my-app.stories.js`,
this.destinationPath(`./stories/${tagName}.stories.js`),
);
await this.copyTemplates(`${__dirname}/templates/static-scaffold-demoing/**/*`);
}
if (this.options.scaffoldFilesFor && this.options.scaffoldFilesFor.includes('testing')) {
this.copyTemplate(
`${__dirname}/templates/_my-app.test.js`,
this.destinationPath(`./test/${tagName}.test.js`),
);
await this.copyTemplates(`${__dirname}/templates/static-scaffold-testing/**/*`);
}
}
async end() {
await super.end();
console.log('');
console.log('You are all set up now!');
console.log('');
console.log('All you need to do is run:');
console.log(` cd ${this.templateData.tagName}`);
console.log(' npm run start');
console.log('');
}
};

View File

@@ -0,0 +1,84 @@
import { LitElement, html, css } from 'lit-element';
import { openWcLogo } from './open-wc-logo.js';
export class <%= className %> extends LitElement {
static get properties() {
return {
title: { type: String },
page: { type: String },
};
}
static get styles() {
return css`
:host {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
font-size: calc(10px + 2vmin);
color: #1a2b42;
max-width: 960px;
margin: 0 auto;
text-align: center;
}
main {
flex-grow: 1;
}
.logo > svg {
margin-top: 36px;
animation: app-logo-spin infinite 20s linear;
}
@keyframes app-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.app-footer {
font-size: calc(12px + 0.5vmin);
align-items: center;
}
.app-footer a {
margin-left: 5px;
}
`;
}
render() {
return html`
<main>
<div class="logo">${openWcLogo}</div>
<h1>My app</h1>
<p>Edit <code>src/<%= className %>.js</code> and save to reload.</p>
<a
class="app-link"
href="https://open-wc.org/developing/#code-examples"
target="_blank"
rel="noopener noreferrer"
>
Code examples
</a>
</main>
<p class="app-footer">
🚽 Made with love by
<a
target="_blank"
rel="noopener noreferrer"
href="https://github.com/open-wc"
>open-wc</a
>.
</p>
`;
}
}

View File

@@ -0,0 +1,3 @@
import { <%= className %> } from './<%= className %>.js';
customElements.define('<%= tagName %>', <%= className %>);

View File

@@ -0,0 +1,11 @@
import { html } from 'lit-html';
import '../src/<%= tagName %>.js';
export default {
title: '<%= tagName %>',
};
export const App = () =>
html`
<<%= tagName %>></<%= tagName %>>
`;

View File

@@ -0,0 +1,22 @@
import { html, fixture, expect } from '@open-wc/testing';
import '../src/<%= tagName %>.js';
describe('<%= className %>', () => {
let element;
beforeEach(async () => {
element = await fixture(html`
<<%= tagName %>></<%= tagName %>>
`);
});
it('renders a h1', () => {
const h1 = element.shadowRoot.querySelector('h1');
expect(h1).to.exist;
expect(h1.textContent).to.equal('My app');
});
it('passes the a11y audit', async () => {
await expect(element).shadowDom.to.be.accessible();
});
});

View File

@@ -0,0 +1,33 @@
import { html } from 'lit-html';
export const openWcLogo = html`
<svg
width="244px"
height="244px"
viewBox="0 0 244 244"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
<stop stop-color="#9B00FF" offset="0%"></stop>
<stop stop-color="#0077FF" offset="100%"></stop>
</linearGradient>
</defs>
<g
id="Page-1"
stroke="none"
stroke-width="1"
fill="none"
fill-rule="evenodd"
>
<path
d="M205.639259,176.936244 C207.430887,174.217233 209.093339,171.405629 210.617884,168.510161 M215.112174,158.724316 C216.385153,155.50304 217.495621,152.199852 218.433474,148.824851 M220.655293,138.874185 C221.231935,135.482212 221.637704,132.03207 221.863435,128.532919 M222,118.131039 C221.860539,114.466419 221.523806,110.85231 221.000113,107.299021 M218.885321,96.8583653 C218.001583,93.4468963 216.942225,90.1061026 215.717466,86.8461994 M211.549484,77.3039459 C209.957339,74.1238901 208.200597,71.0404957 206.290425,68.0649233 M200.180513,59.5598295 C181.848457,36.6639805 153.655709,22 122.036748,22 C66.7879774,22 22,66.771525 22,122 C22,177.228475 66.7879774,222 122.036748,222 C152.914668,222 180.52509,208.015313 198.875424,186.036326"
id="Shape"
stroke="url(#linearGradient-1)"
stroke-width="42.0804674"
></path>
</g>
</svg>
`;

View File

@@ -0,0 +1,14 @@
{
"name": "<%= tagName %>",
"license": "MIT",
"scripts": {
"start": "web-dev-server --app-index index.html --node-resolve --open --watch"
},
"dependencies": {
"lit-html": "^1.0.0",
"lit-element": "^2.0.1"
},
"devDependencies": {
"@web/dev-server": "^0.0.12"
}
}

View File

@@ -0,0 +1,26 @@
{
"version": 2,
"tags": [
{
"name": "<%= tagName %>",
"description": "An application with a title and an action counter",
"properties": [
{
"name": "title",
"type": "String",
"description": "The title of your application",
"default": "Hey there"
},
{
"name": "page",
"type": "String",
"description": "Which page to show",
"default": "main"
}
],
"events": [],
"slots": [],
"cssProperties": []
}
]
}

View File

@@ -0,0 +1,14 @@
module.exports = {
stories: ['../**/stories/*.stories.{js,md,mdx}'],
addons: [
'storybook-prebuilt/addon-knobs/register.js',
'storybook-prebuilt/addon-docs/register.js',
'storybook-prebuilt/addon-viewport/register.js',
],
esDevServer: {
// custom es-dev-server options
nodeResolve: true,
watch: true,
open: true
},
};

View File

@@ -0,0 +1,17 @@
import { addParameters, setCustomElements } from '@open-wc/demoing-storybook';
addParameters({
docs: {
iframeHeight: '200px',
}
});
async function run() {
const customElements = await (
await fetch(new URL('../custom-elements.json', import.meta.url))
).json();
setCustomElements(customElements);
}
run();

View File

@@ -0,0 +1,4 @@
export default {
files: 'test/**/*.test.js',
nodeResolve: true
};

View File

@@ -0,0 +1,30 @@
<p align="center">
<img width="200" src="https://open-wc.org/hero.png"></img>
</p>
## Open-wc Starter App
[![Built with open-wc recommendations](https://img.shields.io/badge/built%20with-open--wc-blue.svg)](https://github.com/open-wc)
## Quickstart
To get started:
```bash
npm init @open-wc
# requires node 10 & npm 6 or higher
```
## Scripts
- `start` runs your app for development, reloading on file changes
- `start:build` runs your app after it has been built using the build command
- `build` builds your app and outputs it in your `dist` directory
- `test` runs your test suite with Web Test Runner
- `lint` runs the linter for your project
## Tooling configs
For most of the tools, the configuration is in the `package.json` to reduce the amount of files in your project.
If you customize the configuration a lot, you can consider moving them to individual files.

View File

@@ -0,0 +1,28 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<meta name="Description" content="Put your description here.">
<base href="/">
<style>
html,
body {
margin: 0;
padding: 0;
font-family: sans-serif;
background-color: #ededed;
}
</style>
<title><%= tagName %></title>
</head>
<body>
<<%= tagName %>></<%= tagName %>>
<script type="module" src="./src/<%= tagName %>.js"></script>
</body>
</html>

View File

@@ -0,0 +1,8 @@
import { executeMixinGenerator } from '../../core.js';
import { gatherMixins } from './gatherMixins.js';
export async function executeViaOptions(options) {
const mixins = gatherMixins(options);
await executeMixinGenerator(mixins, options);
}

View File

@@ -0,0 +1,112 @@
import { WcLitElementMixin, WcLitElementPackageMixin } from '../wc-lit-element/index.js';
import { LintingMixin } from '../linting/index.js';
import { TestingMixin, TestingScaffoldMixin } from '../testing/index.js';
import {
DemoingStorybookMixin,
DemoingStorybookScaffoldMixin,
} from '../demoing-storybook/index.js';
import { BuildingRollupMixin } from '../building-rollup/index.js';
// ts
import { TsWcLitElementMixin, TsWcLitElementPackageMixin } from '../wc-lit-element-ts/index.js';
import { TsLintingMixin } from '../linting-ts/index.js';
import { TsTestingMixin, TsTestingScaffoldMixin } from '../testing-ts/index.js';
import {
TsDemoingStorybookMixin,
TsDemoingStorybookScaffoldMixin,
} from '../demoing-storybook-ts/index.js';
import { TsBuildingRollupMixin } from '../building-rollup-ts/index.js';
export function gatherMixins(options) {
let considerScaffoldFilesFor = false;
const mixins = [];
if (options.type === 'scaffold') {
if (options.typescript === 'true') {
switch (options.scaffoldType) {
case 'wc':
mixins.push(TsWcLitElementPackageMixin);
considerScaffoldFilesFor = true;
break;
case 'wc-lit-element':
mixins.push(TsWcLitElementMixin);
considerScaffoldFilesFor = true;
break;
// no default
}
} else {
switch (options.scaffoldType) {
case 'wc':
mixins.push(WcLitElementPackageMixin);
considerScaffoldFilesFor = true;
break;
case 'wc-lit-element':
mixins.push(WcLitElementMixin);
considerScaffoldFilesFor = true;
break;
// no default
}
}
}
if (options.features && options.features.length > 0) {
if (options.typescript === 'true') {
options.features.forEach(feature => {
if (feature === 'linting') {
mixins.push(TsLintingMixin);
}
if (feature === 'testing') {
mixins.push(TsTestingMixin);
}
if (feature === 'demoing') {
mixins.push(TsDemoingStorybookMixin);
}
if (feature === 'building') {
mixins.push(TsBuildingRollupMixin);
}
});
} else {
options.features.forEach(feature => {
if (feature === 'linting') {
mixins.push(LintingMixin);
}
if (feature === 'testing') {
mixins.push(TestingMixin);
}
if (feature === 'demoing') {
mixins.push(DemoingStorybookMixin);
}
if (feature === 'building') {
mixins.push(BuildingRollupMixin);
}
});
}
}
if (considerScaffoldFilesFor && options.scaffoldFilesFor && options.scaffoldFilesFor.length > 0) {
options.scaffoldFilesFor.forEach(feature => {
if (options.typescript === 'true') {
switch (feature) {
case 'testing':
mixins.push(TsTestingScaffoldMixin);
break;
case 'demoing':
mixins.push(TsDemoingStorybookScaffoldMixin);
break;
// no default
}
} else {
switch (feature) {
case 'testing':
mixins.push(TestingScaffoldMixin);
break;
case 'demoing':
mixins.push(DemoingStorybookScaffoldMixin);
break;
// no default
}
}
});
}
return mixins;
}

View File

@@ -0,0 +1,16 @@
import chalk from 'chalk';
export default `
_.,,,,,,,,,._
.d'' \`\`b. ${chalk.underline('Open Web Components Recommendations')}
.p' Open \`q.
.d' Web Components \`b. Start or upgrade your web component project with
.d' \`b. ease. All our recommendations at your fingertips.
:: ................. ::
\`p. .q' See more details at https://open-wc.org/init/
\`p. open-wc.org .q'
\`b. @openWc .d'
\`q.. ..,' Note: you can exit any time with Ctrl+C or Esc
'',,,,,,,,,,''
`;

147
src/generators/app/index.js Normal file
View File

@@ -0,0 +1,147 @@
/* eslint-disable no-console */
import prompts from 'prompts';
import commandLineArgs from 'command-line-args';
import { executeMixinGenerator } from '../../core.js';
import { AppLitElementMixin } from '../app-lit-element/index.js';
import { TsAppLitElementMixin } from '../app-lit-element-ts/index.js';
import header from './header.js';
import { gatherMixins } from './gatherMixins.js';
/**
* Allows to control the data via command line
*
* example:
* npm init @open-wc --type scaffold --scaffoldType app --tagName foo-bar --installDependencies false
* npm init @open-wc --type upgrade --features linting demoing --tagName foo-bar --scaffoldFilesFor demoing --installDependencies false
*/
const optionDefinitions = [
{ name: 'destinationPath', type: String }, // path
{ name: 'type', type: String }, // scaffold, upgrade
{ name: 'scaffoldType', type: String }, // wc, app
{ name: 'features', type: String, multiple: true }, // linting, testing, demoing, building
{ name: 'scaffoldFilesFor', type: String, multiple: true }, // testing, demoing, building
{ name: 'typescript', type: String },
{ name: 'tagName', type: String },
{ name: 'installDependencies', type: String }, // yarn, npm, false
{ name: 'writeToDisk', type: String }, // true, false
];
const overrides = commandLineArgs(optionDefinitions);
prompts.override(overrides);
export const AppMixin = subclass =>
// eslint-disable-next-line no-shadow
class AppMixin extends subclass {
constructor() {
super();
this.wantsNpmInstall = false;
this.wantsWriteToDisk = false;
this.wantsRecreateInfo = false;
}
async execute() {
console.log(header);
const scaffoldOptions = [];
const questions = [
{
type: 'select',
name: 'type',
message: 'What would you like to do today?',
choices: [
{ title: 'Scaffold a new project', value: 'scaffold' },
{ title: 'Upgrade an existing project', value: 'upgrade' },
],
},
{
type: (prev, all) => (all.type === 'scaffold' ? 'select' : null),
name: 'scaffoldType',
message: 'What would you like to scaffold?',
choices: [
{ title: 'Web Component', value: 'wc' },
{ title: 'Application', value: 'app' },
],
},
{
type: (prev, all) =>
all.scaffoldType === 'wc' || all.scaffoldType === 'app' || all.type === 'upgrade'
? 'multiselect'
: null,
name: 'features',
message: 'What would you like to add?',
choices: (prev, all) =>
[
{ title: 'Linting (eslint & prettier)', value: 'linting' },
{ title: 'Testing (web-test-runner)', value: 'testing' },
{ title: 'Demoing (storybook)', value: 'demoing' },
all.scaffoldType !== 'wc' && {
title: 'Building (rollup)',
value: 'building',
},
].filter(_ => !!_),
onState: state => {
state.value.forEach(meta => {
if (meta.selected === true && meta.value !== 'linting') {
scaffoldOptions.push({
title: meta.title,
value: meta.value,
});
}
});
},
},
{
type: 'select',
name: 'typescript',
message: 'Would you like to use typescript?',
choices: [
{ title: 'No', value: 'false' },
{ title: 'Yes', value: 'true' },
],
},
{
type: () => (scaffoldOptions.length > 0 ? 'multiselect' : null),
name: 'scaffoldFilesFor',
message: 'Would you like to scaffold examples files for?',
choices: scaffoldOptions,
},
{
type: 'text',
name: 'tagName',
message: 'What is the tag name of your application/web component?',
validate: tagName =>
!/^([a-z])(?!.*[<>])(?=.*-).+$/.test(tagName)
? 'You need a minimum of two words separated by dashes (e.g. foo-bar)'
: true,
},
];
/**
* {
* type: 'scaffold',
* scaffoldType: 'wc',
* features: [ 'testing', 'building' ],
* scaffoldFilesFor: [ 'testing' ],
* tagName: 'foo-bar',
* installDependencies: 'false'
* }
*/
this.options = await prompts(questions, {
onCancel: () => {
process.exit();
},
});
const mixins = gatherMixins(this.options);
// app is separate to prevent circular imports
if (this.options.type === 'scaffold' && this.options.scaffoldType === 'app') {
if (this.options.typescript === 'true') {
mixins.push(TsAppLitElementMixin);
} else {
mixins.push(AppLitElementMixin);
}
}
await executeMixinGenerator(mixins, this.options);
}
};
export default AppMixin;

View File

@@ -0,0 +1,13 @@
export const TsBuildingRollupMixin = subclass =>
class extends subclass {
async execute() {
await super.execute();
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
await this.copyTemplates(`${__dirname}/templates/static/**/*`);
}
};

View File

@@ -0,0 +1,12 @@
{
"scripts": {
"build": "rimraf dist && tsc && rollup -c rollup.config.js",
"start:build": "npm run build && web-dev-server --root-dir dist --app-index index.html --open --compatibility none"
},
"devDependencies": {
"@open-wc/building-rollup": "^1.0.0",
"deepmerge": "^4.2.2",
"rimraf": "^2.6.3",
"rollup": "^2.3.4"
}
}

View File

@@ -0,0 +1,31 @@
import merge from 'deepmerge';
// use createSpaConfig for bundling a Single Page App
import { createSpaConfig } from '@open-wc/building-rollup';
// use createBasicConfig to do regular JS to JS bundling
// import { createBasicConfig } from '@open-wc/building-rollup';
const baseConfig = createSpaConfig({
// use the outputdir option to modify where files are output
// outputDir: 'dist',
// if you need to support older browsers, such as IE11, set the legacyBuild
// option to generate an additional build just for this browser
// legacyBuild: true,
// development mode creates a non-minified build for debugging or development
developmentMode: process.env.ROLLUP_WATCH === 'true',
// set to true to inject the service worker registration into your index.html
injectServiceWorker: false,
});
export default merge(baseConfig, {
// if you use createSpaConfig, you can use your index.html as entrypoint,
// any <script type="module"> inside will be bundled by rollup
input: './index.html',
// alternatively, you can use your JS as entrypoint for rollup and
// optionally set a HTML template manually
// input: './app.js',
});

View File

@@ -0,0 +1,13 @@
export const BuildingRollupMixin = subclass =>
class extends subclass {
async execute() {
await super.execute();
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
await this.copyTemplates(`${__dirname}/templates/static/**/*`);
}
};

View File

@@ -0,0 +1,12 @@
{
"scripts": {
"build": "rimraf dist && rollup -c rollup.config.js",
"start:build": "npm run build && web-dev-server --root-dir dist --app-index index.html --open --compatibility none"
},
"devDependencies": {
"@open-wc/building-rollup": "^1.0.0",
"deepmerge": "^4.2.2",
"rimraf": "^2.6.3",
"rollup": "^2.3.4"
}
}

View File

@@ -0,0 +1,31 @@
import merge from 'deepmerge';
// use createSpaConfig for bundling a Single Page App
import { createSpaConfig } from '@open-wc/building-rollup';
// use createBasicConfig to do regular JS to JS bundling
// import { createBasicConfig } from '@open-wc/building-rollup';
const baseConfig = createSpaConfig({
// use the outputdir option to modify where files are output
// outputDir: 'dist',
// if you need to support older browsers, such as IE11, set the legacyBuild
// option to generate an additional build just for this browser
// legacyBuild: true,
// development mode creates a non-minified build for debugging or development
developmentMode: process.env.ROLLUP_WATCH === 'true',
// set to true to inject the service worker registration into your index.html
injectServiceWorker: false,
});
export default merge(baseConfig, {
// if you use createSpaConfig, you can use your index.html as entrypoint,
// any <script type="module"> inside will be bundled by rollup
input: './index.html',
// alternatively, you can use your JS as entrypoint for rollup and
// optionally set a HTML template manually
// input: './app.js',
});

View File

@@ -0,0 +1,22 @@
export const CommonRepoMixin = subclass =>
class extends subclass {
async execute() {
this.templateData = {
...this.templateData,
year: new Date().getFullYear(),
};
await super.execute();
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
// write and rename .gitignore
this.copyTemplate(`${__dirname}/templates/_gitignore`, this.destinationPath(`.gitignore`));
// copy all other files
await this.copyTemplates(`${__dirname}/templates/static/**/*`);
}
};

View File

@@ -0,0 +1,21 @@
## editors
/.idea
/.vscode
## system files
.DS_Store
## npm
/node_modules/
/npm-debug.log
## testing
/coverage/
## temp folders
/.tmp/
# build
/_site/
/dist/
/out-tsc/

View File

@@ -0,0 +1,7 @@
{
"name": "<%= tagName %>",
"version": "0.0.0",
"description": "Webcomponent <%= tagName %> following open-wc recommendations",
"author": "<%= tagName %>",
"license": "MIT"
}

View File

@@ -0,0 +1,29 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# Change these settings to your own preference
indent_style = space
indent_size = 2
# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[*.json]
indent_size = 2
[*.{html,js,md}]
block_comment_start = /**
block_comment = *
block_comment_end = */

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) <%= year %> <%= tagName %>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,22 @@
/* eslint-disable max-classes-per-file */
export const TsDemoingStorybookMixin = subclass =>
class extends subclass {
async execute() {
await super.execute();
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
await this.copyTemplates(`${__dirname}/templates/static/**/*`);
}
};
export const TsDemoingStorybookScaffoldMixin = subclass =>
class extends subclass {
async execute() {
await super.execute();
await this.copyTemplates(`${__dirname}/templates/static-scaffold/**/*`);
}
};

View File

@@ -0,0 +1,9 @@
{
"scripts": {
"storybook": "concurrently --kill-others --names tsc,storybook \"npm run tsc:watch\" \"start-storybook --node-resolve --watch --open\"",
"storybook:build": "build-storybook"
},
"devDependencies": {
"@open-wc/demoing-storybook": "^2.0.0"
}
}

View File

@@ -0,0 +1,48 @@
```js script
import { html } from '@open-wc/demoing-storybook';
import '../dist/<%= tagName %>.js';
export default {
title: '<%= className %>',
component: '<%= tagName %>',
options: { selectedPanel: "storybookjs/knobs/panel" },
};
```
# <%= className %>
A component for...
## Features:
- a
- b
- ...
## How to use
### Installation
```bash
yarn add <%= tagName %>
```
```js
import '<%= tagName %>/<%= tagName %>.js';
```
```js preview-story
export const Simple = () => html`
<<%= tagName %>></<%= tagName %>>
`;
```
## Variations
###### Custom Title
```js preview-story
export const CustomTitle = () => html`
<<%= tagName %> title="Hello World"></<%= tagName %>>
`;
```

View File

@@ -0,0 +1,14 @@
module.exports = {
stories: ['../stories/**/*.stories.{js,md,mdx}'],
addons: [
'storybook-prebuilt/addon-knobs/register.js',
'storybook-prebuilt/addon-docs/register.js',
'storybook-prebuilt/addon-viewport/register.js',
],
esDevServer: {
// custom es-dev-server options
nodeResolve: true,
watch: true,
open: true
},
};

View File

@@ -0,0 +1,17 @@
import { addParameters, setCustomElements } from '@open-wc/demoing-storybook';
addParameters({
docs: {
iframeHeight: '200px',
}
});
async function run() {
const customElements = await (
await fetch(new URL('../custom-elements.json', import.meta.url))
).json();
setCustomElements(customElements);
}
run();

View File

@@ -0,0 +1,22 @@
/* eslint-disable max-classes-per-file */
export const DemoingStorybookMixin = subclass =>
class extends subclass {
async execute() {
await super.execute();
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
await this.copyTemplates(`${__dirname}/templates/static/**/*`);
}
};
export const DemoingStorybookScaffoldMixin = subclass =>
class extends subclass {
async execute() {
await super.execute();
await this.copyTemplates(`${__dirname}/templates/static-scaffold/**/*`);
}
};

View File

@@ -0,0 +1,9 @@
{
"scripts": {
"storybook": "start-storybook",
"storybook:build": "build-storybook"
},
"devDependencies": {
"@open-wc/demoing-storybook": "^2.0.0"
}
}

View File

@@ -0,0 +1,48 @@
```js script
import { html } from '@open-wc/demoing-storybook';
import '../<%= tagName %>.js';
export default {
title: '<%= className %>',
component: '<%= tagName %>',
options: { selectedPanel: "storybookjs/knobs/panel" },
};
```
# <%= className %>
A component for...
## Features:
- a
- b
- ...
## How to use
### Installation
```bash
yarn add <%= tagName %>
```
```js
import '<%= tagName %>/<%= tagName %>.js';
```
```js preview-story
export const Simple = () => html`
<<%= tagName %>></<%= tagName %>>
`;
```
## Variations
###### Custom Title
```js preview-story
export const CustomTitle = () => html`
<<%= tagName %> title="Hello World"></<%= tagName %>>
`;
```

View File

@@ -0,0 +1,14 @@
module.exports = {
stories: ['../stories/**/*.stories.{js,md,mdx}'],
addons: [
'storybook-prebuilt/addon-knobs/register.js',
'storybook-prebuilt/addon-docs/register.js',
'storybook-prebuilt/addon-viewport/register.js',
],
esDevServer: {
// custom es-dev-server options
nodeResolve: true,
watch: true,
open: true
},
};

View File

@@ -0,0 +1,17 @@
import { addParameters, setCustomElements } from '@open-wc/demoing-storybook';
addParameters({
docs: {
iframeHeight: '200px',
}
});
async function run() {
const customElements = await (
await fetch(new URL('../custom-elements.json', import.meta.url))
).json();
setCustomElements(customElements);
}
run();

View File

@@ -0,0 +1,3 @@
# do not show lock files while doing git diff
package-lock.json -diff
yarn.lock -diff

View File

@@ -0,0 +1,13 @@
export const LintingCommitlintMixin = subclass =>
class extends subclass {
async execute() {
await super.execute();
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
await this.copyTemplates(`${__dirname}/templates/static/**/*`);
}
};

View File

@@ -0,0 +1,6 @@
{
"devDependencies": {
"@commitlint/cli": "^7.0.0",
"@commitlint/config-conventional": "^7.0.0"
}
}

View File

@@ -0,0 +1,3 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
};

View File

@@ -0,0 +1,16 @@
export const TsLintingEsLintMixin = subclass =>
class extends subclass {
async execute() {
await super.execute();
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
this.copyTemplate(
`${__dirname}/templates/_.eslintrc.js`,
this.destinationPath('.eslintrc.js'),
);
}
};

View File

@@ -0,0 +1,20 @@
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'import', 'html'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/errors',
'plugin:import/warnings',
],
rules: {
// disable the rule for all files
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'import/named': 'off',
'import/no-unresolved': 'off',
'import/extensions': ['error', 'always', { ignorePackages: true }],
},
};

View File

@@ -0,0 +1,17 @@
{
"scripts": {
"lint:eslint": "eslint --ext .ts,.html . --ignore-path .gitignore",
"format:eslint": "eslint --ext .ts,.html . --fix --ignore-path .gitignore"
},
"devDependencies": {
"eslint": "^6.1.0",
"@open-wc/eslint-config": "^2.0.0",
"@typescript-eslint/eslint-plugin": "^2.20.0",
"@typescript-eslint/parser": "^2.20.0"
},
"eslintConfig": {
"extends": [
"@open-wc/eslint-config"
]
}
}

View File

@@ -0,0 +1,11 @@
export const LintingEsLintMixin = subclass =>
class extends subclass {
async execute() {
await super.execute();
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
}
};

View File

@@ -0,0 +1,15 @@
{
"scripts": {
"lint:eslint": "eslint --ext .js,.html . --ignore-path .gitignore",
"format:eslint": "eslint --ext .js,.html . --fix --ignore-path .gitignore"
},
"devDependencies": {
"eslint": "^6.1.0",
"@open-wc/eslint-config": "^2.0.0"
},
"eslintConfig": {
"extends": [
"@open-wc/eslint-config"
]
}
}

View File

@@ -0,0 +1,11 @@
export const TsLintingPrettierMixin = subclass =>
class extends subclass {
async execute() {
await super.execute();
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
}
};

View File

@@ -0,0 +1,19 @@
{
"scripts": {
"lint:prettier": "prettier \"**/*.js\" \"**/*.ts\" --check --ignore-path .gitignore",
"format:prettier": "prettier \"**/*.js\" \"**/*.ts\" --write --ignore-path .gitignore"
},
"devDependencies": {
"prettier": "^2.0.4",
"eslint-config-prettier": "^6.11.0"
},
"eslintConfig": {
"extends": [
"eslint-config-prettier"
]
},
"prettier": {
"singleQuote": true,
"arrowParens": "avoid"
}
}

View File

@@ -0,0 +1,11 @@
export const LintingPrettierMixin = subclass =>
class extends subclass {
async execute() {
await super.execute();
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
}
};

View File

@@ -0,0 +1,19 @@
{
"scripts": {
"lint:prettier": "prettier \"**/*.js\" --check --ignore-path .gitignore",
"format:prettier": "prettier \"**/*.js\" --write --ignore-path .gitignore"
},
"devDependencies": {
"prettier": "^2.0.4",
"eslint-config-prettier": "^6.11.0"
},
"eslintConfig": {
"extends": [
"eslint-config-prettier"
]
},
"prettier": {
"singleQuote": true,
"arrowParens": "avoid"
}
}

View File

@@ -0,0 +1,16 @@
/* eslint-disable no-console */
import { TsLintingEsLintMixin } from '../linting-eslint-ts/index.js';
import { TsLintingPrettierMixin } from '../linting-prettier-ts/index.js';
export const TsLintingMixin = subclass =>
class extends TsLintingPrettierMixin(TsLintingEsLintMixin(subclass)) {
async execute() {
await super.execute();
// extend package.json
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
}
};

View File

@@ -0,0 +1,22 @@
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.ts": [
"eslint --fix",
"prettier --write",
"git add"
]
},
"scripts": {
"lint": "npm run lint:eslint && npm run lint:prettier",
"format": "npm run format:eslint && npm run format:prettier"
},
"devDependencies": {
"husky": "^1.0.0",
"lint-staged": "^10.0.0"
}
}

View File

@@ -0,0 +1,13 @@
export const LintingTypesJsMixin = subclass =>
class extends subclass {
async execute() {
await super.execute();
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
await this.copyTemplates(`${__dirname}/templates/static/**/*`);
}
};

View File

@@ -0,0 +1,8 @@
{
"scripts": {
"lint:types": "tsc"
},
"devDependencies": {
"typescript": "~4.0.3"
}
}

View File

@@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"lib": ["es2017", "dom"],
"allowJs": true,
"checkJs": true,
"noEmit": true,
"strict": false,
"noImplicitThis": true,
"alwaysStrict": true,
"esModuleInterop": true
},
"include": [
"**/*.js",
"node_modules/@open-wc/**/*.js"
],
"exclude": [
"node_modules/!(@open-wc)"
]
}

View File

@@ -0,0 +1,16 @@
/* eslint-disable no-console */
import { LintingEsLintMixin } from '../linting-eslint/index.js';
import { LintingPrettierMixin } from '../linting-prettier/index.js';
export const LintingMixin = subclass =>
class extends LintingPrettierMixin(LintingEsLintMixin(subclass)) {
async execute() {
await super.execute();
// extend package.json
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
}
};

View File

@@ -0,0 +1,22 @@
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.js": [
"eslint --fix",
"prettier --write",
"git add"
]
},
"scripts": {
"lint": "npm run lint:eslint && npm run lint:prettier",
"format": "npm run format:eslint && npm run format:prettier"
},
"devDependencies": {
"husky": "^1.0.0",
"lint-staged": "^10.0.0"
}
}

View File

@@ -0,0 +1,27 @@
/* eslint-disable max-classes-per-file */
import { TsTestingWebTestRunnerMixin } from '../testing-wtr-ts/index.js';
export const TsTestingMixin = subclass =>
class extends TsTestingWebTestRunnerMixin(subclass) {
async execute() {
await super.execute();
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
}
};
export const TsTestingScaffoldMixin = subclass =>
class extends subclass {
async execute() {
await super.execute();
const { tagName } = this.templateData;
this.copyTemplate(
`${__dirname}/templates/_my-el.test.ts`,
this.destinationPath(`test/${tagName}.test.ts`),
);
}
};

View File

@@ -0,0 +1,40 @@
import { html, fixture, expect } from '@open-wc/testing';
import {<%= className %>} from '../src/<%= className %>.js';
import '../<%= tagName %>.js';
describe('<%= className %>', () => {
it('has a default title "Hey there" and counter 5', async () => {
const el: <%= className %> = await fixture(html`
<<%= tagName %>></<%= tagName %>>
`);
expect(el.title).to.equal('Hey there');
expect(el.counter).to.equal(5);
});
it('increases the counter on button click', async () => {
const el: <%= className %> = await fixture(html`
<<%= tagName %>></<%= tagName %>>
`);
el.shadowRoot!.querySelector('button')!.click();
expect(el.counter).to.equal(6);
});
it('can override the title via attribute', async () => {
const el: <%= className %> = await fixture(html`
<<%= tagName %> title="attribute title"></<%= tagName %>>
`);
expect(el.title).to.equal('attribute title');
});
it('passes the a11y audit', async () => {
const el: <%= className %> = await fixture(html`
<<%= tagName %>></<%= tagName %>>
`);
await expect(el).shadowDom.to.be.accessible();
});
});

View File

@@ -0,0 +1,5 @@
{
"devDependencies": {
"@open-wc/testing": "^2.0.0"
}
}

View File

@@ -0,0 +1,13 @@
export const TestingWallabyMixin = subclass =>
class extends subclass {
async execute() {
await super.execute();
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
await this.copyTemplates(`${__dirname}/templates/static/**/*`);
}
};

View File

@@ -0,0 +1,5 @@
{
"devDependencies": {
"@open-wc/testing-wallaby": "^0.1.3"
}
}

View File

@@ -0,0 +1,23 @@
const wallabyWebpack = require('wallaby-webpack'); // eslint-disable-line import/no-extraneous-dependencies
const path = require('path');
const wallabyPostprocessor = wallabyWebpack({
resolve: {
modules: [path.resolve(__dirname, 'bower_components'), 'node_modules'],
},
});
module.exports = () => ({
files: [{ pattern: '*.js', load: false }, '!wallaby.js', '!*.config.js', '!*.conf.js'],
tests: [{ pattern: 'test/*.test.js', load: false }],
testFramework: 'mocha',
debug: true,
env: {
kind: 'chrome',
},
postprocessor: wallabyPostprocessor,
setup: () => {
// required to trigger test loading
window.__moduleBundler.loadTests();
},
});

View File

@@ -0,0 +1,13 @@
export const TsTestingWebTestRunnerMixin = subclass =>
class extends subclass {
async execute() {
await super.execute();
this.copyTemplateJsonInto(
`${__dirname}/templates/_package.json`,
this.destinationPath('package.json'),
);
await this.copyTemplates(`${__dirname}/templates/static/**/*`);
}
};

View File

@@ -0,0 +1,9 @@
{
"scripts": {
"test": "tsc && web-test-runner --coverage",
"test:watch": "web-test-runner --watch"
},
"devDependencies": {
"@web/test-runner": "^0.7.41"
}
}

View File

@@ -0,0 +1,4 @@
export default {
files: 'dist/**/test/**/*.test.js',
nodeResolve: true
};

Some files were not shown because too many files have changed in this diff Show More