Skip to content

Commit

Permalink
add create-lwc-plugin package
Browse files Browse the repository at this point in the history
  • Loading branch information
SlicedSilver committed Aug 30, 2023
1 parent 86da482 commit 0c5984c
Show file tree
Hide file tree
Showing 50 changed files with 2,360 additions and 1 deletion.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
/website/build/**

/plugin-examples
/packages/create-lwc-plugin
2 changes: 2 additions & 0 deletions packages/create-lwc-plugin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
dist
46 changes: 46 additions & 0 deletions packages/create-lwc-plugin/BUILDING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Local Development of the create-lwc-plugin

The minimal supported version of [NodeJS](https://nodejs.org/) for development is 18.

1. Install the dependencies

```shell
npm install
```

2. Create a development stub

```shell
npm run dev
```

3. Running the CLI locally

```shell
node index.js
```

## Publishing new version

1. Install the dependencies

```shell
npm install
```

2. Bump the version number in the `package.json`
3. Build the package

```shell
npm run prepublishOnly
```

4. Run `npx publint@latest` and ensure that there aren't any issues with the generated `package.json`.
5. Publish the package
```shell
npm publish
```
Hint: append `--dry-run` to the end of the publish command to see the results of
the publish command without actually uploading the package to NPM.
304 changes: 304 additions & 0 deletions packages/create-lwc-plugin/LICENSE

Large diffs are not rendered by default.

82 changes: 82 additions & 0 deletions packages/create-lwc-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# create-lwc-plugin

**create-lwc-plugin** is an npm package designed to simplify the process of
creating a new plugin for Lightweight Charts™. With this generator, you can
quickly scaffold a project from a template for either

- a Drawing primitive plugin, or
- a Custom series plugin.

By using this wizard-like tool, you can customize the initial setup of their
plugin project by answering a few questions. This allows for a seamless and
efficient starting point, saving valuable time and effort.

Whether you are developing a new Drawing primitive plugin or a Custom series
plugin for Lightweight Charts, this generator provides a structured and
organized foundation. It ensures that your plugin adheres to the best practices
and conventions of Lightweight Charts, making it easier to develop, maintain,
and contribute to the community.

Getting started with your Lightweight Charts plugin development has never been
easier. Let the Lightweight Charts™ Plugin Scaffold Generator
(`create-lwc-plugin`) handle the initial setup, so you can focus on creating
outstanding plugins for Lightweight Charts™.

✨ Need some examples for inspiration? Check out the
[plugin-examples](https://github.com/tradingview/lightweight-charts/tree/master/plugin-examples)
folder in the Lightweight Charts repo.

## Scaffolding Your First Lightweight Charts™ Plugin

With NPM:

```bash
npm create lwc-plugin@latest
```

With Yarn:

```bash
yarn create lwc-plugin
```

With PNPM:

```bash
pnpm create lwc-plugin
```

## Using the generated project

### Running Locally (during development)

```shell
npm install
npm run dev
```

Visit `localhost:5173` in the browser.

### Compiling the Plugin

```shell
npm run compile
```

Check the output in the `dist` folder.

### Publishing To NPM

You can configure the contents of the package's `package.json` within the
`compile.mjs` script.

Once you have compiled the plugin (see above section) then you can publish the
package to NPM with these commands:

```shell
cd dist
npm publish
```

Hint: append `--dry-run` to the end of the publish command to see the results of
the publish command without actually uploading the package to NPM.
13 changes: 13 additions & 0 deletions packages/create-lwc-plugin/build.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { defineBuildConfig } from 'unbuild';

export default defineBuildConfig({
entries: ['src/index'],
clean: true,
rollup: {
inlineDependencies: true,
esbuild: {
target: 'node18',
minify: true,
},
},
});
3 changes: 3 additions & 0 deletions packages/create-lwc-plugin/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env node

import './dist/index.mjs';
41 changes: 41 additions & 0 deletions packages/create-lwc-plugin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "create-lwc-plugin",
"version": "1.0.0",
"type": "module",
"license": "MIT",
"author": "TradingView",
"description": "Wizard-like CLI tool for scaffolding a new plugin for Lightweight Charts™",
"keywords": ["lightweight-charts", "lwc-plugin", "plugins"],
"bin": {
"create-lwc-plugin": "index.js"
},
"files": [
"index.js",
"template-*",
"dist"
],
"scripts": {
"dev": "unbuild --stub && echo 'now run `node index.js`'",
"build": "unbuild",
"typecheck": "tsc --noEmit",
"prepublishOnly": "npm run build"
},
"engines": {
"node": "^18.0.0 || >=20.0.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tradingview/lightweight-charts.git",
"directory": "packages/create-lwc-plugin"
},
"bugs": {
"url": "https://github.com/slicedsilver/create-lwc-plugin/issues"
},
"homepage": "https://tradingview.github.io/lightweight-charts/",
"devDependencies": {
"@clack/prompts": "^0.7.0",
"@types/node": "^20.5.1",
"picocolors": "^1.0.0",
"unbuild": "^1.2.1"
}
}
54 changes: 54 additions & 0 deletions packages/create-lwc-plugin/src/helpers/io.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import fs from 'node:fs';
import path from 'node:path';

export function formatTargetDir(targetDir: string | undefined) {
return targetDir?.trim().replace(/\/+$/g, '');
}

export function copy(src: string, dest: string, contentReplacer?: (content: string) => string) {
const stat = fs.statSync(src);
if (stat.isDirectory()) {
copyDir(src, dest, contentReplacer);
} else {
const content = fs.readFileSync(src).toString();
fs.writeFileSync(dest, contentReplacer ? contentReplacer(content) : content);
}
}

export function copyDir(srcDir: string, destDir: string, contentReplacer?: (content: string) => string) {
fs.mkdirSync(destDir, { recursive: true });
for (const file of fs.readdirSync(srcDir)) {
const srcFile = path.resolve(srcDir, file);
const destFile = path.resolve(destDir, file);
copy(srcFile, destFile, contentReplacer);
}
}

export function dirExists(dir: string) {
return fs.existsSync(dir);
}

export function isEmpty(path: string) {
const files = fs.readdirSync(path);
return files.length === 0 || (files.length === 1 && files[0] === '.git');
}

export function emptyDir(dir: string) {
if (!fs.existsSync(dir)) {
return;
}
for (const file of fs.readdirSync(dir)) {
if (file === '.git') {
continue;
}
fs.rmSync(path.resolve(dir, file), { recursive: true, force: true });
}
}

// editFile(path.resolve(root, `vite.config.${isTs ? 'ts' : 'js'}`), content => {
// return content.replace('@vitejs/plugin-react', '@vitejs/plugin-react-swc');
// });
export function editFile(file: string, callback: (content: string) => string) {
const content = fs.readFileSync(file, 'utf-8');
fs.writeFileSync(file, callback(content), 'utf-8');
}
14 changes: 14 additions & 0 deletions packages/create-lwc-plugin/src/helpers/package.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
function pkgFromUserAgent(userAgent: string | undefined) {
if (!userAgent) return undefined;
const pkgSpec = userAgent.split(' ')[0];
const pkgSpecArr = pkgSpec.split('/');
return {
name: pkgSpecArr[0],
version: pkgSpecArr[1],
};
}

export function getPkgManagerName() {
const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent);
return pkgInfo ? pkgInfo.name : 'npm';
}
14 changes: 14 additions & 0 deletions packages/create-lwc-plugin/src/helpers/validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export function isValidPackageName(projectName: string) {
return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(
projectName
);
}

export function toValidPackageName(projectName: string) {
return projectName
.trim()
.toLowerCase()
.replace(/\s+/g, '-')
.replace(/^[._]/, '')
.replace(/[^a-z\d\-~]+/g, '-');
}
101 changes: 101 additions & 0 deletions packages/create-lwc-plugin/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { intro, outro, spinner, cancel } from '@clack/prompts';
import color from 'picocolors';
import { Answers, askQuestions } from './questions';
import { copy } from './helpers/io';

const cwd = process.cwd();

const renameFiles: Record<string, string | undefined> = {
_gitignore: '.gitignore',
};

async function init() {
console.log();
intro(color.inverse(' create-lwc-plugin '));

let answers: Answers;
try {
answers = await askQuestions();
} catch (e: unknown) {
if (e instanceof Error) {
cancel(e.message);
}
return process.exit(0);
}

const s = spinner();

s.start('Building your new plugin project');
const root = path.join(cwd, answers.targetFolderPath);
if (answers.targetFolderPath) {
fs.mkdirSync(root, { recursive: true });
}
const templateDir = path.resolve(
fileURLToPath(import.meta.url),
'../..',
`template-${answers.projectType}`
);
const commonTemplateDir = path.resolve(
fileURLToPath(import.meta.url),
'../..',
`template-common`
);

const entryName = 'template-entry';
const newEntryName = answers.packageName.replace(/lwc-plugin-/, '');
const entryFileName = `${entryName}.ts`;
const newEntryFileName = `${newEntryName}.ts`;

const contentsReplacer = (content: string): string => {
const result = content
.replaceAll(entryName, newEntryName)
.replace(/_PLUGINNAME_/g, answers.name)
.replace(/_CLASSNAME_/g, answers.typeName)
.replace(/_PACKAGENAME_/g, answers.packageName);
if (answers.includeHints) {
return result;
}
// Comments starting with '//*' are considered 'hints'
return result.replace(/.*\/\/\*.*\r?\n/g, '');
};

const write = (dir: string, file: string, content?: string) => {
const targetPath = path.join(root, renameFiles[file] ?? file);
if (content) {
fs.writeFileSync(targetPath, contentsReplacer(content));
} else {
copy(path.join(dir, file), targetPath, contentsReplacer);
}
};

const files = fs.readdirSync(templateDir);
for (const file of files) {
write(templateDir, file);
}

const commonFiles = fs.readdirSync(commonTemplateDir);
for (const file of commonFiles.filter(f => f !== 'package.json')) {
write(commonTemplateDir, file);
}

const pkg = JSON.parse(
fs.readFileSync(path.join(commonTemplateDir, `package.json`), 'utf-8')
);

pkg.name = answers.packageName;

write(root, 'package.json', JSON.stringify(pkg, null, 2) + '\n');

fs.renameSync(path.join(root, 'src', entryFileName), path.join(root, 'src', newEntryFileName));

s.stop('Built your new plugin project');

outro("You're all set!");
}

init().catch(e => {
console.error(e);
});
Loading

0 comments on commit 0c5984c

Please sign in to comment.