@tsparticles/cli 1.6.4 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -76,14 +76,16 @@ async function buildDistFiles(basePath) {
76
76
  const contents = await fs_extra_1.default.readFile(file.path, "utf8");
77
77
  await fs_extra_1.default.writeFile(file.path, contents.replaceAll("__VERSION__", `"${pkgInfo.version}"`), "utf8");
78
78
  }
79
- for await (const file of (0, klaw_1.default)(path_1.default.join(distPath, "cjs"))) {
80
- await fs_extra_1.default.rename(file.path, file.path.replace(/\.js$/, ".cjs"));
79
+ /*for await (const file of klaw(path.join(distPath, "cjs"))) {
80
+ await fs.rename(file.path, file.path.replace(/\.js$/, ".cjs"));
81
81
  }
82
- for await (const file of (0, klaw_1.default)(path_1.default.join(distPath, "esm"))) {
83
- await fs_extra_1.default.rename(file.path, file.path.replace(/\.js$/, ".mjs"));
82
+
83
+ for await (const file of klaw(path.join(distPath, "esm"))) {
84
+ await fs.rename(file.path, file.path.replace(/\.js$/, ".mjs"));
84
85
  }
85
- await fs_extra_1.default.writeFile(path_1.default.join(distPath, "cjs", "package.json"), `{ "type": "commonjs" }`);
86
- await fs_extra_1.default.writeFile(path_1.default.join(distPath, "esm", "package.json"), `{ "type": "module" }`);
86
+
87
+ await fs.writeFile(path.join(distPath, "cjs", "package.json"), `{ "type": "commonjs" }`);
88
+ await fs.writeFile(path.join(distPath, "esm", "package.json"), `{ "type": "module" }`);*/
87
89
  res = true;
88
90
  }
89
91
  catch (e) {
@@ -2,8 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createCommand = void 0;
4
4
  const commander_1 = require("commander");
5
+ const plugin_1 = require("./plugin/plugin");
5
6
  const preset_1 = require("./preset/preset");
7
+ const shape_1 = require("./shape/shape");
6
8
  const createCommand = new commander_1.Command("create");
7
9
  exports.createCommand = createCommand;
8
10
  createCommand.description("Create a new tsParticles project");
11
+ createCommand.addCommand(plugin_1.pluginCommand);
9
12
  createCommand.addCommand(preset_1.presetCommand);
13
+ createCommand.addCommand(shape_1.shapeCommand);
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createPluginTemplate = void 0;
7
+ const string_utils_1 = require("../../utils/string-utils");
8
+ const template_utils_1 = require("../../utils/template-utils");
9
+ const fs_extra_1 = __importDefault(require("fs-extra"));
10
+ const path_1 = __importDefault(require("path"));
11
+ /**
12
+ * Updates the index file with the correct function name
13
+ * @param destPath - The path where the project is located
14
+ * @param name - The name of the project
15
+ */
16
+ async function updateIndexFile(destPath, name) {
17
+ const indexPath = path_1.default.resolve(destPath, "src", "index.ts"), index = await fs_extra_1.default.readFile(indexPath, "utf-8"), capitalizedName = (0, string_utils_1.capitalize)((0, string_utils_1.capitalize)(name, "-"), " "), camelizedName = (0, string_utils_1.camelize)(capitalizedName), indexFunctionRegex = /loadTemplatePlugin/g, replacedFuncText = index.replace(indexFunctionRegex, `load${capitalizedName}Plugin`), indexNameRegex = /"#template#"/g, replacedNameText = replacedFuncText.replace(indexNameRegex, `"${camelizedName}"`);
18
+ await fs_extra_1.default.writeFile(indexPath, replacedNameText);
19
+ }
20
+ /**
21
+ * Updates the plugin package file
22
+ * @param destPath - The path where the project is located
23
+ * @param name - The name of the project
24
+ * @param description - The description of the project
25
+ * @param repoUrl - The repository url
26
+ */
27
+ async function updatePluginPackageFile(destPath, name, description, repoUrl) {
28
+ const camelizedName = (0, string_utils_1.camelize)((0, string_utils_1.camelize)(name, "-"), " "), dashedName = (0, string_utils_1.dash)(camelizedName);
29
+ (0, template_utils_1.updatePackageFile)(destPath, `"tsparticles-plugin-${dashedName}"`, description, `"tsparticles.plugin.${camelizedName}.min.js"`, repoUrl);
30
+ }
31
+ /**
32
+ * Updates the plugin package dist file
33
+ * @param destPath - The path where the project is located
34
+ * @param name - The name of the project
35
+ * @param description - The description of the project
36
+ * @param repoUrl - The repository url
37
+ */
38
+ async function updatePluginPackageDistFile(destPath, name, description, repoUrl) {
39
+ const camelizedName = (0, string_utils_1.camelize)((0, string_utils_1.camelize)(name, "-"), " "), dashedName = (0, string_utils_1.dash)(camelizedName);
40
+ (0, template_utils_1.updatePackageDistFile)(destPath, `"tsparticles-plugin-${dashedName}"`, description, `"tsparticles.plugin.${camelizedName}.min.js"`, repoUrl);
41
+ }
42
+ /**
43
+ * Updates the plugin readme file
44
+ * @param destPath - The path where the project is located
45
+ * @param name - The name of the project
46
+ * @param description - The description of the project
47
+ * @param repoUrl - The repository url
48
+ */
49
+ async function updateReadmeFile(destPath, name, description, repoUrl) {
50
+ const readmePath = path_1.default.resolve(destPath, "README.md"), readme = await fs_extra_1.default.readFile(readmePath, "utf-8"), capitalizedName = (0, string_utils_1.capitalize)((0, string_utils_1.capitalize)(name, "-"), " "), camelizedName = (0, string_utils_1.camelize)(capitalizedName), dashedName = (0, string_utils_1.dash)(camelizedName), readmeDescriptionRegex = /tsParticles Template Plugin/g, replacedDescriptionText = readme.replace(readmeDescriptionRegex, `tsParticles ${description} Plugin`), readmePackageNameRegex = /tsparticles-plugin-template/g, replacedPackageNameText = replacedDescriptionText.replace(readmePackageNameRegex, `tsparticles-plugin-${dashedName}`), readmeFileNameRegex = /tsparticles\.plugin\.template(\.bundle)?\.min\.js/g, replacedFileNameText = replacedPackageNameText.replace(readmeFileNameRegex, `tsparticles.plugin.${camelizedName}$1.min.js`), readmeFunctionNameRegex = /loadTemplatePlugin/g, replacedFunctionNameText = replacedFileNameText.replace(readmeFunctionNameRegex, `load${capitalizedName}Plugin`), readmeMiniDescriptionRegex = /\[tsParticles]\(https:\/\/github.com\/matteobruni\/tsparticles\) additional template plugin\./g, replacedMiniDescriptionText = replacedFunctionNameText.replace(readmeMiniDescriptionRegex, `[tsParticles](https://github.com/matteobruni/tsparticles) additional ${name} plugin.`), readmeUsageRegex = /plugin\.type: "template"/g, replacedUsageText = replacedMiniDescriptionText.replace(readmeUsageRegex, `plugin.type: "${camelizedName}`), sampleImageRegex = /!\[demo]\(https:\/\/raw.githubusercontent.com\/tsparticles\/plugin-template\/main\/images\/sample.png\)/g, repoPath = repoUrl.includes("github.com")
51
+ ? repoUrl.substring(repoUrl.indexOf("github.com/") + 11, repoUrl.indexOf(".git"))
52
+ : "tsparticles/plugin-template", replacedText = replacedUsageText.replace(sampleImageRegex, `![demo](https://raw.githubusercontent.com/${repoPath}/main/images/sample.png)`);
53
+ await fs_extra_1.default.writeFile(readmePath, replacedText);
54
+ }
55
+ /**
56
+ * Updates the plugin webpack file
57
+ * @param destPath - The path where the project is located
58
+ * @param name - The name of the project
59
+ * @param description - The description of the project
60
+ */
61
+ async function updatePluginWebpackFile(destPath, name, description) {
62
+ await (0, template_utils_1.updateWebpackFile)(destPath, (0, string_utils_1.camelize)((0, string_utils_1.capitalize)((0, string_utils_1.capitalize)(name, "-"), " ")), `tsParticles ${description} Plugin`, "loadParticlesPlugin");
63
+ }
64
+ /**
65
+ * Creates the plugin project
66
+ * @param name - The name of the project
67
+ * @param description - The description of the project
68
+ * @param repoUrl - The repository url
69
+ * @param destPath - The path where the project is located
70
+ */
71
+ async function createPluginTemplate(name, description, repoUrl, destPath) {
72
+ const sourcePath = path_1.default.resolve(__dirname, "..", "..", "..", "files", "create-plugin");
73
+ await (0, template_utils_1.copyEmptyTemplateFiles)(destPath);
74
+ await fs_extra_1.default.copy(sourcePath, destPath, {
75
+ overwrite: true,
76
+ filter: template_utils_1.copyFilter,
77
+ });
78
+ await updateIndexFile(destPath, name);
79
+ await updatePluginPackageFile(destPath, name, description, repoUrl);
80
+ await updatePluginPackageDistFile(destPath, name, description, repoUrl);
81
+ await updateReadmeFile(destPath, name, description, repoUrl);
82
+ await updatePluginWebpackFile(destPath, name, description);
83
+ (0, template_utils_1.runInstall)(destPath);
84
+ (0, template_utils_1.runBuild)(destPath);
85
+ }
86
+ exports.createPluginTemplate = createPluginTemplate;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.pluginCommand = void 0;
7
+ const prompts_1 = __importDefault(require("prompts"));
8
+ const commander_1 = require("commander");
9
+ const string_utils_1 = require("../../utils/string-utils");
10
+ const create_plugin_1 = require("./create-plugin");
11
+ const child_process_1 = require("child_process");
12
+ const fs_extra_1 = __importDefault(require("fs-extra"));
13
+ const path_1 = __importDefault(require("path"));
14
+ const pluginCommand = new commander_1.Command("plugin");
15
+ exports.pluginCommand = pluginCommand;
16
+ pluginCommand.description("Create a new tsParticles plugin");
17
+ pluginCommand.argument("<destination>", "Destination folder");
18
+ pluginCommand.action(async (destination) => {
19
+ let repoUrl;
20
+ const destPath = path_1.default.resolve(path_1.default.join(process.cwd(), destination)), destExists = await fs_extra_1.default.pathExists(destPath);
21
+ if (destExists) {
22
+ const destContents = await fs_extra_1.default.readdir(destPath), destContentsNoGit = destContents.filter(t => t !== ".git" && t !== ".gitignore");
23
+ if (destContentsNoGit.length) {
24
+ throw new Error("Destination folder already exists and is not empty");
25
+ }
26
+ }
27
+ await fs_extra_1.default.ensureDir(destPath);
28
+ try {
29
+ repoUrl = (0, child_process_1.execSync)("git config --get remote.origin.url").toString();
30
+ }
31
+ catch {
32
+ repoUrl = "";
33
+ }
34
+ const initialName = destPath.split(path_1.default.sep).pop(), questions = [
35
+ {
36
+ type: "text",
37
+ name: "name",
38
+ message: "What is the name of the plugin?",
39
+ validate: (value) => (value ? true : "The name can't be empty"),
40
+ initial: initialName,
41
+ },
42
+ {
43
+ type: "text",
44
+ name: "description",
45
+ message: "What is the description of the plugin?",
46
+ validate: (value) => (value ? true : "The description can't be empty"),
47
+ initial: (0, string_utils_1.capitalize)(initialName || ""),
48
+ },
49
+ {
50
+ type: "text",
51
+ name: "repositoryUrl",
52
+ message: "What is the repository URL? (optional)",
53
+ initial: repoUrl.trim(),
54
+ },
55
+ ];
56
+ const { name, description, repositoryUrl } = await (0, prompts_1.default)(questions);
57
+ (0, create_plugin_1.createPluginTemplate)(name.trim(), description.trim(), repositoryUrl.trim(), destPath);
58
+ });
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createShapeTemplate = void 0;
7
+ const string_utils_1 = require("../../utils/string-utils");
8
+ const template_utils_1 = require("../../utils/template-utils");
9
+ const fs_extra_1 = __importDefault(require("fs-extra"));
10
+ const path_1 = __importDefault(require("path"));
11
+ /**
12
+ * Updates the index file with the correct function name
13
+ * @param destPath - The path where the project is located
14
+ * @param name - The name of the project
15
+ */
16
+ async function updateIndexFile(destPath, name) {
17
+ const indexPath = path_1.default.resolve(destPath, "src", "index.ts"), index = await fs_extra_1.default.readFile(indexPath, "utf-8"), capitalizedName = (0, string_utils_1.capitalize)((0, string_utils_1.capitalize)(name, "-"), " "), camelizedName = (0, string_utils_1.camelize)(capitalizedName), indexFunctionRegex = /loadTemplateShape/g, replacedFuncText = index.replace(indexFunctionRegex, `load${capitalizedName}Shape`), indexNameRegex = /"#template#"/g, replacedNameText = replacedFuncText.replace(indexNameRegex, `"${camelizedName}"`);
18
+ await fs_extra_1.default.writeFile(indexPath, replacedNameText);
19
+ }
20
+ /**
21
+ * Updates the shape package file
22
+ * @param destPath - The path where the project is located
23
+ * @param name - The name of the project
24
+ * @param description - The description of the project
25
+ * @param repoUrl - The repository url
26
+ */
27
+ async function updateShapePackageFile(destPath, name, description, repoUrl) {
28
+ const camelizedName = (0, string_utils_1.camelize)((0, string_utils_1.camelize)(name, "-"), " "), dashedName = (0, string_utils_1.dash)(camelizedName);
29
+ (0, template_utils_1.updatePackageFile)(destPath, `"tsparticles-shape-${dashedName}"`, description, `"tsparticles.shape.${camelizedName}.min.js"`, repoUrl);
30
+ }
31
+ /**
32
+ * Updates the shape package dist file
33
+ * @param destPath - The path where the project is located
34
+ * @param name - The name of the project
35
+ * @param description - The description of the project
36
+ * @param repoUrl - The repository url
37
+ */
38
+ async function updateShapePackageDistFile(destPath, name, description, repoUrl) {
39
+ const camelizedName = (0, string_utils_1.camelize)((0, string_utils_1.camelize)(name, "-"), " "), dashedName = (0, string_utils_1.dash)(camelizedName);
40
+ (0, template_utils_1.updatePackageDistFile)(destPath, `"tsparticles-shape-${dashedName}"`, description, `"tsparticles.shape.${camelizedName}.min.js"`, repoUrl);
41
+ }
42
+ /**
43
+ * Updates the shape readme file
44
+ * @param destPath - The path where the project is located
45
+ * @param name - The name of the project
46
+ * @param description - The description of the project
47
+ * @param repoUrl - The repository url
48
+ */
49
+ async function updateReadmeFile(destPath, name, description, repoUrl) {
50
+ const readmePath = path_1.default.resolve(destPath, "README.md"), readme = await fs_extra_1.default.readFile(readmePath, "utf-8"), capitalizedName = (0, string_utils_1.capitalize)((0, string_utils_1.capitalize)(name, "-"), " "), camelizedName = (0, string_utils_1.camelize)(capitalizedName), dashedName = (0, string_utils_1.dash)(camelizedName), readmeDescriptionRegex = /tsParticles Template Shape/g, replacedDescriptionText = readme.replace(readmeDescriptionRegex, `tsParticles ${description} Shape`), readmePackageNameRegex = /tsparticles-shape-template/g, replacedPackageNameText = replacedDescriptionText.replace(readmePackageNameRegex, `tsparticles-shape-${dashedName}`), readmeFileNameRegex = /tsparticles\.shape\.template(\.bundle)?\.min\.js/g, replacedFileNameText = replacedPackageNameText.replace(readmeFileNameRegex, `tsparticles.shape.${camelizedName}$1.min.js`), readmeFunctionNameRegex = /loadTemplateShape/g, replacedFunctionNameText = replacedFileNameText.replace(readmeFunctionNameRegex, `load${capitalizedName}Shape`), readmeMiniDescriptionRegex = /\[tsParticles]\(https:\/\/github.com\/matteobruni\/tsparticles\) additional template shape\./g, replacedMiniDescriptionText = replacedFunctionNameText.replace(readmeMiniDescriptionRegex, `[tsParticles](https://github.com/matteobruni/tsparticles) additional ${name} shape.`), readmeUsageRegex = /shape\.type: "template"/g, replacedUsageText = replacedMiniDescriptionText.replace(readmeUsageRegex, `shape.type: "${camelizedName}`), sampleImageRegex = /!\[demo]\(https:\/\/raw.githubusercontent.com\/tsparticles\/shape-template\/main\/images\/sample.png\)/g, repoPath = repoUrl.includes("github.com")
51
+ ? repoUrl.substring(repoUrl.indexOf("github.com/") + 11, repoUrl.indexOf(".git"))
52
+ : "tsparticles/shape-template", replacedText = replacedUsageText.replace(sampleImageRegex, `![demo](https://raw.githubusercontent.com/${repoPath}/main/images/sample.png)`);
53
+ await fs_extra_1.default.writeFile(readmePath, replacedText);
54
+ }
55
+ /**
56
+ * Updates the shape webpack file
57
+ * @param destPath - The path where the project is located
58
+ * @param name - The name of the project
59
+ * @param description - The description of the project
60
+ */
61
+ async function updateShapeWebpackFile(destPath, name, description) {
62
+ await (0, template_utils_1.updateWebpackFile)(destPath, (0, string_utils_1.camelize)((0, string_utils_1.capitalize)((0, string_utils_1.capitalize)(name, "-"), " ")), `tsParticles ${description} Shape`, "loadParticlesShape");
63
+ }
64
+ /**
65
+ * Creates the shape project
66
+ * @param name - The name of the project
67
+ * @param description - The description of the project
68
+ * @param repoUrl - The repository url
69
+ * @param destPath - The path where the project is located
70
+ */
71
+ async function createShapeTemplate(name, description, repoUrl, destPath) {
72
+ const sourcePath = path_1.default.resolve(__dirname, "..", "..", "..", "files", "create-shape");
73
+ await (0, template_utils_1.copyEmptyTemplateFiles)(destPath);
74
+ await fs_extra_1.default.copy(sourcePath, destPath, {
75
+ overwrite: true,
76
+ filter: template_utils_1.copyFilter,
77
+ });
78
+ await updateIndexFile(destPath, name);
79
+ await updateShapePackageFile(destPath, name, description, repoUrl);
80
+ await updateShapePackageDistFile(destPath, name, description, repoUrl);
81
+ await updateReadmeFile(destPath, name, description, repoUrl);
82
+ await updateShapeWebpackFile(destPath, name, description);
83
+ (0, template_utils_1.runInstall)(destPath);
84
+ (0, template_utils_1.runBuild)(destPath);
85
+ }
86
+ exports.createShapeTemplate = createShapeTemplate;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.shapeCommand = void 0;
7
+ const prompts_1 = __importDefault(require("prompts"));
8
+ const commander_1 = require("commander");
9
+ const string_utils_1 = require("../../utils/string-utils");
10
+ const create_shape_1 = require("./create-shape");
11
+ const child_process_1 = require("child_process");
12
+ const fs_extra_1 = __importDefault(require("fs-extra"));
13
+ const path_1 = __importDefault(require("path"));
14
+ const shapeCommand = new commander_1.Command("shape");
15
+ exports.shapeCommand = shapeCommand;
16
+ shapeCommand.description("Create a new tsParticles shape");
17
+ shapeCommand.argument("<destination>", "Destination folder");
18
+ shapeCommand.action(async (destination) => {
19
+ let repoUrl;
20
+ const destPath = path_1.default.resolve(path_1.default.join(process.cwd(), destination)), destExists = await fs_extra_1.default.pathExists(destPath);
21
+ if (destExists) {
22
+ const destContents = await fs_extra_1.default.readdir(destPath), destContentsNoGit = destContents.filter(t => t !== ".git" && t !== ".gitignore");
23
+ if (destContentsNoGit.length) {
24
+ throw new Error("Destination folder already exists and is not empty");
25
+ }
26
+ }
27
+ await fs_extra_1.default.ensureDir(destPath);
28
+ try {
29
+ repoUrl = (0, child_process_1.execSync)("git config --get remote.origin.url").toString();
30
+ }
31
+ catch {
32
+ repoUrl = "";
33
+ }
34
+ const initialName = destPath.split(path_1.default.sep).pop(), questions = [
35
+ {
36
+ type: "text",
37
+ name: "name",
38
+ message: "What is the name of the shape?",
39
+ validate: (value) => (value ? true : "The name can't be empty"),
40
+ initial: initialName,
41
+ },
42
+ {
43
+ type: "text",
44
+ name: "description",
45
+ message: "What is the description of the shape?",
46
+ validate: (value) => (value ? true : "The description can't be empty"),
47
+ initial: (0, string_utils_1.capitalize)(initialName || ""),
48
+ },
49
+ {
50
+ type: "text",
51
+ name: "repositoryUrl",
52
+ message: "What is the repository URL? (optional)",
53
+ initial: repoUrl.trim(),
54
+ },
55
+ ];
56
+ const { name, description, repositoryUrl } = await (0, prompts_1.default)(questions);
57
+ (0, create_shape_1.createShapeTemplate)(name.trim(), description.trim(), repositoryUrl.trim(), destPath);
58
+ });
@@ -0,0 +1,74 @@
1
+ [![banner](https://particles.js.org/images/banner2.png)](https://particles.js.org)
2
+
3
+ # tsParticles Template Plugin
4
+
5
+ [![jsDelivr](https://data.jsdelivr.com/v1/package/npm/tsparticles-plugin-template/badge)](https://www.jsdelivr.com/package/npm/tsparticles-plugin-template)
6
+ [![npmjs](https://badge.fury.io/js/tsparticles-plugin-template.svg)](https://www.npmjs.com/package/tsparticles-plugin-template)
7
+ [![npmjs](https://img.shields.io/npm/dt/tsparticles-plugin-template)](https://www.npmjs.com/package/tsparticles-plugin-template) [![GitHub Sponsors](https://img.shields.io/github/sponsors/matteobruni)](https://github.com/sponsors/matteobruni)
8
+
9
+ [tsParticles](https://github.com/matteobruni/tsparticles) plugin for particles template.
10
+
11
+ ## How to use it
12
+
13
+ ### CDN / Vanilla JS / jQuery
14
+
15
+ The CDN/Vanilla version JS has one required file in vanilla configuration:
16
+
17
+ Including the `tsparticles.plugin.template.min.js` file will export the function to load the plugin:
18
+
19
+ ```javascript
20
+ loadTemplatePlugin;
21
+ ```
22
+
23
+ ### Usage
24
+
25
+ Once the scripts are loaded you can set up `tsParticles` and the plugin like this:
26
+
27
+ ```javascript
28
+ (async () => {
29
+ await loadTemplatePlugin(tsParticles);
30
+
31
+ await tsParticles.load({
32
+ id: "tsparticles",
33
+ options: {
34
+ /* options */
35
+ },
36
+ });
37
+ })();
38
+ ```
39
+
40
+ ### ESM / CommonJS
41
+
42
+ This package is compatible also with ES or CommonJS modules, firstly this needs to be installed, like this:
43
+
44
+ ```shell
45
+ $ npm install tsparticles-plugin-template
46
+ ```
47
+
48
+ or
49
+
50
+ ```shell
51
+ $ yarn add tsparticles-plugin-template
52
+ ```
53
+
54
+ Then you need to import it in the app, like this:
55
+
56
+ ```javascript
57
+ const { tsParticles } = require("tsparticles-engine");
58
+ const { loadTemplatePlugin } = require("tsparticles-plugin-template");
59
+
60
+ (async () => {
61
+ await loadTemplatePlugin(tsParticles);
62
+ })();
63
+ ```
64
+
65
+ or
66
+
67
+ ```javascript
68
+ import { tsParticles } from "tsparticles-engine";
69
+ import { loadTemplatePlugin } from "tsparticles-plugin-template";
70
+
71
+ (async () => {
72
+ await loadTemplatePlugin(tsParticles);
73
+ })();
74
+ ```
@@ -0,0 +1,15 @@
1
+ import { type Container, type Engine, type IContainerPlugin } from "tsparticles-engine";
2
+
3
+ export class PluginInstance implements IContainerPlugin {
4
+ private readonly _container;
5
+ private readonly _engine;
6
+
7
+ constructor(container: Container, engine: Engine) {
8
+ this._container = container;
9
+ this._engine = engine;
10
+ }
11
+
12
+ async init(): Promise<void> {
13
+ // add your plugin initialization here
14
+ }
15
+ }
@@ -0,0 +1,39 @@
1
+ import type { Container, Engine, IPlugin, ISourceOptions, Options } from "tsparticles-engine";
2
+ import { PluginInstance } from "./PluginInstance";
3
+
4
+ /**
5
+ */
6
+ class Plugin implements IPlugin {
7
+ readonly id;
8
+
9
+ private readonly _engine;
10
+
11
+ constructor(engine: Engine) {
12
+ this.id = "#template#";
13
+
14
+ this._engine = engine;
15
+ }
16
+
17
+ getPlugin(container: Container): PluginInstance {
18
+ return new PluginInstance(container, this._engine);
19
+ }
20
+
21
+ loadOptions(_options: Options, _source?: ISourceOptions): void {
22
+ if (!this.needsPlugin()) {
23
+ return;
24
+ }
25
+
26
+ // Load your options here
27
+ }
28
+
29
+ needsPlugin(_options?: ISourceOptions): boolean {
30
+ return true; // add your condition here
31
+ }
32
+ }
33
+
34
+ /**
35
+ * @param engine - The engine instance
36
+ */
37
+ export async function loadTemplatePlugin(engine: Engine): Promise<void> {
38
+ await engine.addPlugin(new Plugin(engine));
39
+ }
@@ -40,9 +40,11 @@ A bundled script can also be used, this will include every needed plugin needed
40
40
  Once the scripts are loaded you can set up `tsParticles` like this:
41
41
 
42
42
  ```javascript
43
- tsParticles.load("tsparticles", {
44
- preset: "template",
45
- });
43
+ (async () => {
44
+ await tsParticles.load("tsparticles", {
45
+ preset: "template",
46
+ });
47
+ })();
46
48
  ```
47
49
 
48
50
  #### Customization
@@ -51,14 +53,16 @@ tsParticles.load("tsparticles", {
51
53
  You can override all the options defining the properties like in any standard `tsParticles` installation.
52
54
 
53
55
  ```javascript
54
- tsParticles.load("tsparticles", {
55
- particles: {
56
- shape: {
57
- type: "square",
58
- },
59
- },
60
- preset: "template",
61
- });
56
+ (async () => {
57
+ await tsParticles.load("tsparticles", {
58
+ particles: {
59
+ shape: {
60
+ type: "square"
61
+ }
62
+ },
63
+ preset: "template"
64
+ });
65
+ })();
62
66
  ```
63
67
 
64
68
  Like in the sample above, the circles will be replaced by squares.
@@ -71,19 +75,19 @@ This sample uses the class component syntax, but you can use hooks as well (if t
71
75
 
72
76
  ```javascript
73
77
  import Particles from "react-particles";
74
- import { Main } from "tsparticles";
78
+ import { Engine } from "tsparticles-engine";
75
79
  import { loadTemplatePreset } from "tsparticles-preset-template";
76
80
 
77
81
  export class ParticlesContainer extends React.PureComponent<IProps> {
78
82
  // this customizes the component tsParticles installation
79
- customInit(main: Main) {
83
+ async customInit(engine: Engine) {
80
84
  // this adds the preset to tsParticles, you can safely use the
81
- loadTemplatePreset(main);
85
+ await loadTemplatePreset(engine);
82
86
  }
83
87
 
84
88
  render() {
85
89
  const options = {
86
- preset: "template", // also "template" is accepted
90
+ preset: "template",
87
91
  };
88
92
 
89
93
  return <Particles options={options} init={this.customInit} />;
@@ -100,25 +104,25 @@ _The syntax for `Vue.js 2.x` and `3.x` is the same_
100
104
  ```
101
105
 
102
106
  ```js
103
- function particlesInit(main: Main) {
104
- loadTemplatePreset(main);
107
+ async function particlesInit(engine: Engine) {
108
+ await loadTemplatePreset(main);
105
109
  }
106
110
  ```
107
111
 
108
112
  ### Angular
109
113
 
110
114
  ```html
115
+
111
116
  <ng-particles
112
- [id]="id"
113
- [options]="particlesOptions"
114
- (particlesLoaded)="particlesLoaded($event)"
115
- (particlesInit)="particlesInit($event)"
117
+ [id]="id"
118
+ [options]="particlesOptions"
119
+ [particlesInit]="particlesInit"
116
120
  ></ng-particles>
117
121
  ```
118
122
 
119
123
  ```ts
120
- function particlesInit(main: Main): void {
121
- loadTemplatePreset(main);
124
+ async function particlesInit(engine: Engine): Promise<void> {
125
+ loadTemplatePreset(engine);
122
126
  }
123
127
  ```
124
128
 
@@ -129,12 +133,12 @@ function particlesInit(main: Main): void {
129
133
  <Particles
130
134
  id="tsparticles"
131
135
  url="{particlesUrl}"
132
- on:particlesInit="{onParticlesInit}"
136
+ particlesInit="{particlesInit}"
133
137
  />
134
138
  ```
135
139
 
136
140
  ```js
137
- let onParticlesInit = (main) => {
138
- loadTemplatePreset(main);
141
+ let particlesInit = async (engine) => {
142
+ await loadTemplatePreset(engine);
139
143
  };
140
144
  ```
@@ -0,0 +1,75 @@
1
+ [![banner](https://particles.js.org/images/banner3.png)](https://particles.js.org)
2
+
3
+ # tsParticles Template Shape
4
+
5
+ [![jsDelivr](https://data.jsdelivr.com/v1/package/npm/tsparticles-shape-template/badge)](https://www.jsdelivr.com/package/npm/tsparticles-shape-template)
6
+ [![npmjs](https://badge.fury.io/js/tsparticles-shape-template.svg)](https://www.npmjs.com/package/tsparticles-shape-template)
7
+ [![npmjs](https://img.shields.io/npm/dt/tsparticles-shape-template)](https://www.npmjs.com/package/tsparticles-shape-template) [![GitHub Sponsors](https://img.shields.io/github/sponsors/matteobruni)](https://github.com/sponsors/matteobruni)
8
+
9
+ [tsParticles](https://github.com/matteobruni/tsparticles) additional template shape.
10
+
11
+ ## How to use it
12
+
13
+ ### CDN / Vanilla JS / jQuery
14
+
15
+ The CDN/Vanilla version JS has one required file in vanilla configuration:
16
+
17
+ Including the `tsparticles.shape.template.min.js` file will export the function to load the shape:
18
+
19
+ ```text
20
+ loadTemplateShape
21
+ ```
22
+
23
+ ### Usage
24
+
25
+ Once the scripts are loaded you can set up `tsParticles` and the shape like this:
26
+
27
+ ```javascript
28
+ (async () => {
29
+ await loadTemplateShape(tsParticles);
30
+
31
+ await tsParticles.load({
32
+ id: "tsparticles",
33
+ options: {
34
+ /* options */
35
+ /* here you can use particles.shape.type: "template" */
36
+ },
37
+ });
38
+ })();
39
+ ```
40
+
41
+ ### ESM / CommonJS
42
+
43
+ This package is compatible also with ES or CommonJS modules, firstly this needs to be installed, like this:
44
+
45
+ ```shell
46
+ $ npm install tsparticles-shape-template
47
+ ```
48
+
49
+ or
50
+
51
+ ```shell
52
+ $ yarn add tsparticles-shape-template
53
+ ```
54
+
55
+ Then you need to import it in the app, like this:
56
+
57
+ ```javascript
58
+ const { tsParticles } = require("tsparticles-engine");
59
+ const { loadTemplateShape } = require("tsparticles-shape-template");
60
+
61
+ (async () => {
62
+ await loadTemplateShape(tsParticles);
63
+ })();
64
+ ```
65
+
66
+ or
67
+
68
+ ```javascript
69
+ import { tsParticles } from "tsparticles-engine";
70
+ import { loadTemplateShape } from "tsparticles-shape-template";
71
+
72
+ (async () => {
73
+ await loadTemplateShape(tsParticles);
74
+ })();
75
+ ```
@@ -0,0 +1,19 @@
1
+ import type { IDelta, IShapeDrawer, Particle } from "tsparticles-engine";
2
+
3
+ export class ShapeDrawer implements IShapeDrawer {
4
+ draw(_context: CanvasRenderingContext2D,
5
+ _particle: Particle,
6
+ _radius: number,
7
+ _opacity: number,
8
+ _delta: IDelta,
9
+ _pixelRatio: number): void {
10
+ // draw the particle using the context
11
+ // which is already centered in the particle position
12
+ // colors are already handles, just draw the shape
13
+ // the bounds are -radius to radius
14
+ // delta is the frame time difference between the last frame and this one, in ms, use it for animated shapes
15
+ // pixelRatio is the canvas ratio used by the tsParticles instance, you may need it for density-independent shapes
16
+ // the parameters have an underscore prefix because they're not used in this example
17
+ // the underscore prefix can be removed for used parameters, the unused ones can be removed too
18
+ }
19
+ }
@@ -0,0 +1,9 @@
1
+ import type { Engine } from "tsparticles-engine";
2
+ import { ShapeDrawer } from "./ShapeDrawer";
3
+
4
+ /**
5
+ * @param engine - the engine instance to load the shape into
6
+ */
7
+ export async function loadTemplateShape(engine: Engine): Promise<void> {
8
+ await engine.addShape("#template#", new ShapeDrawer());
9
+ }
@@ -83,7 +83,7 @@
83
83
  "prettier": "@tsparticles/prettier-config",
84
84
  "devDependencies": {
85
85
  "@babel/core": "^7.22.5",
86
- "@tsparticles/cli": "1.6.4",
86
+ "@tsparticles/cli": "1.7.0",
87
87
  "@tsparticles/eslint-config": "^1.13.0",
88
88
  "@tsparticles/prettier-config": "^1.10.0",
89
89
  "@tsparticles/tsconfig": "^1.13.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsparticles/cli",
3
- "version": "1.6.4",
3
+ "version": "1.7.0",
4
4
  "license": "MIT",
5
5
  "bin": {
6
6
  "tsparticles-cli": "dist/cli.js"
@@ -10,31 +10,31 @@
10
10
  },
11
11
  "prettier": "@tsparticles/prettier-config",
12
12
  "dependencies": {
13
- "@tsparticles/eslint-config": "^1.13.3",
13
+ "@tsparticles/eslint-config": "^1.14.0",
14
14
  "@tsparticles/prettier-config": "^1.10.0",
15
- "@tsparticles/tsconfig": "^1.13.0",
16
- "@tsparticles/webpack-plugin": "^1.15.5",
17
- "@typescript-eslint/eslint-plugin": "^5.60.0",
18
- "@typescript-eslint/parser": "^5.60.0",
15
+ "@tsparticles/tsconfig": "^1.14.0",
16
+ "@tsparticles/webpack-plugin": "^1.16.0",
17
+ "@typescript-eslint/eslint-plugin": "^5.60.1",
18
+ "@typescript-eslint/parser": "^5.60.1",
19
19
  "commander": "^11.0.0",
20
20
  "eslint": "^8.43.0",
21
21
  "eslint-config-prettier": "^8.8.0",
22
- "eslint-plugin-jsdoc": "^46.2.6",
22
+ "eslint-plugin-jsdoc": "^46.4.2",
23
23
  "eslint-plugin-tsdoc": "^0.2.17",
24
24
  "fs-extra": "^11.1.1",
25
25
  "klaw": "^4.1.0",
26
- "path-scurry": "^1.9.2",
26
+ "path-scurry": "^1.10.0",
27
27
  "prettier": "^2.8.8",
28
28
  "prompts": "^2.4.2",
29
29
  "rimraf": "^5.0.1",
30
- "typescript": "^5.1.3",
31
- "webpack": "^5.87.0"
30
+ "typescript": "^5.1.6",
31
+ "webpack": "^5.88.1"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/eslint": "^8.40.2",
35
35
  "@types/fs-extra": "^11.0.1",
36
36
  "@types/klaw": "^3.0.3",
37
- "@types/node": "^20.3.1",
37
+ "@types/node": "^20.3.2",
38
38
  "@types/prettier": "^2.7.3",
39
39
  "@types/prompts": "^2.4.4"
40
40
  },
@@ -71,7 +71,7 @@ export async function buildDistFiles(basePath: string): Promise<boolean> {
71
71
  await fs.writeFile(file.path, contents.replaceAll("__VERSION__", `"${pkgInfo.version}"`), "utf8");
72
72
  }
73
73
 
74
- for await (const file of klaw(path.join(distPath, "cjs"))) {
74
+ /*for await (const file of klaw(path.join(distPath, "cjs"))) {
75
75
  await fs.rename(file.path, file.path.replace(/\.js$/, ".cjs"));
76
76
  }
77
77
 
@@ -80,7 +80,7 @@ export async function buildDistFiles(basePath: string): Promise<boolean> {
80
80
  }
81
81
 
82
82
  await fs.writeFile(path.join(distPath, "cjs", "package.json"), `{ "type": "commonjs" }`);
83
- await fs.writeFile(path.join(distPath, "esm", "package.json"), `{ "type": "module" }`);
83
+ await fs.writeFile(path.join(distPath, "esm", "package.json"), `{ "type": "module" }`);*/
84
84
 
85
85
  res = true;
86
86
  } catch (e) {
@@ -1,9 +1,14 @@
1
1
  import { Command } from "commander";
2
+ import { pluginCommand } from "./plugin/plugin";
2
3
  import { presetCommand } from "./preset/preset";
4
+ import { shapeCommand } from "./shape/shape";
3
5
 
4
6
  const createCommand = new Command("create");
5
7
 
6
8
  createCommand.description("Create a new tsParticles project");
9
+
10
+ createCommand.addCommand(pluginCommand);
7
11
  createCommand.addCommand(presetCommand);
12
+ createCommand.addCommand(shapeCommand);
8
13
 
9
14
  export { createCommand };
@@ -0,0 +1,178 @@
1
+ import { camelize, capitalize, dash } from "../../utils/string-utils";
2
+ import {
3
+ copyEmptyTemplateFiles,
4
+ copyFilter,
5
+ runBuild,
6
+ runInstall,
7
+ updatePackageDistFile,
8
+ updatePackageFile,
9
+ updateWebpackFile,
10
+ } from "../../utils/template-utils";
11
+ import fs from "fs-extra";
12
+ import path from "path";
13
+
14
+ /**
15
+ * Updates the index file with the correct function name
16
+ * @param destPath - The path where the project is located
17
+ * @param name - The name of the project
18
+ */
19
+ async function updateIndexFile(destPath: string, name: string): Promise<void> {
20
+ const indexPath = path.resolve(destPath, "src", "index.ts"),
21
+ index = await fs.readFile(indexPath, "utf-8"),
22
+ capitalizedName = capitalize(capitalize(name, "-"), " "),
23
+ camelizedName = camelize(capitalizedName),
24
+ indexFunctionRegex = /loadTemplatePlugin/g,
25
+ replacedFuncText = index.replace(indexFunctionRegex, `load${capitalizedName}Plugin`),
26
+ indexNameRegex = /"#template#"/g,
27
+ replacedNameText = replacedFuncText.replace(indexNameRegex, `"${camelizedName}"`);
28
+
29
+ await fs.writeFile(indexPath, replacedNameText);
30
+ }
31
+
32
+ /**
33
+ * Updates the plugin package file
34
+ * @param destPath - The path where the project is located
35
+ * @param name - The name of the project
36
+ * @param description - The description of the project
37
+ * @param repoUrl - The repository url
38
+ */
39
+ async function updatePluginPackageFile(
40
+ destPath: string,
41
+ name: string,
42
+ description: string,
43
+ repoUrl: string
44
+ ): Promise<void> {
45
+ const camelizedName = camelize(camelize(name, "-"), " "),
46
+ dashedName = dash(camelizedName);
47
+
48
+ updatePackageFile(
49
+ destPath,
50
+ `"tsparticles-plugin-${dashedName}"`,
51
+ description,
52
+ `"tsparticles.plugin.${camelizedName}.min.js"`,
53
+ repoUrl
54
+ );
55
+ }
56
+
57
+ /**
58
+ * Updates the plugin package dist file
59
+ * @param destPath - The path where the project is located
60
+ * @param name - The name of the project
61
+ * @param description - The description of the project
62
+ * @param repoUrl - The repository url
63
+ */
64
+ async function updatePluginPackageDistFile(
65
+ destPath: string,
66
+ name: string,
67
+ description: string,
68
+ repoUrl: string
69
+ ): Promise<void> {
70
+ const camelizedName = camelize(camelize(name, "-"), " "),
71
+ dashedName = dash(camelizedName);
72
+
73
+ updatePackageDistFile(
74
+ destPath,
75
+ `"tsparticles-plugin-${dashedName}"`,
76
+ description,
77
+ `"tsparticles.plugin.${camelizedName}.min.js"`,
78
+ repoUrl
79
+ );
80
+ }
81
+
82
+ /**
83
+ * Updates the plugin readme file
84
+ * @param destPath - The path where the project is located
85
+ * @param name - The name of the project
86
+ * @param description - The description of the project
87
+ * @param repoUrl - The repository url
88
+ */
89
+ async function updateReadmeFile(destPath: string, name: string, description: string, repoUrl: string): Promise<void> {
90
+ const readmePath = path.resolve(destPath, "README.md"),
91
+ readme = await fs.readFile(readmePath, "utf-8"),
92
+ capitalizedName = capitalize(capitalize(name, "-"), " "),
93
+ camelizedName = camelize(capitalizedName),
94
+ dashedName = dash(camelizedName),
95
+ readmeDescriptionRegex = /tsParticles Template Plugin/g,
96
+ replacedDescriptionText = readme.replace(readmeDescriptionRegex, `tsParticles ${description} Plugin`),
97
+ readmePackageNameRegex = /tsparticles-plugin-template/g,
98
+ replacedPackageNameText = replacedDescriptionText.replace(
99
+ readmePackageNameRegex,
100
+ `tsparticles-plugin-${dashedName}`
101
+ ),
102
+ readmeFileNameRegex = /tsparticles\.plugin\.template(\.bundle)?\.min\.js/g,
103
+ replacedFileNameText = replacedPackageNameText.replace(
104
+ readmeFileNameRegex,
105
+ `tsparticles.plugin.${camelizedName}$1.min.js`
106
+ ),
107
+ readmeFunctionNameRegex = /loadTemplatePlugin/g,
108
+ replacedFunctionNameText = replacedFileNameText.replace(
109
+ readmeFunctionNameRegex,
110
+ `load${capitalizedName}Plugin`
111
+ ),
112
+ readmeMiniDescriptionRegex =
113
+ /\[tsParticles]\(https:\/\/github.com\/matteobruni\/tsparticles\) additional template plugin\./g,
114
+ replacedMiniDescriptionText = replacedFunctionNameText.replace(
115
+ readmeMiniDescriptionRegex,
116
+ `[tsParticles](https://github.com/matteobruni/tsparticles) additional ${name} plugin.`
117
+ ),
118
+ readmeUsageRegex = /plugin\.type: "template"/g,
119
+ replacedUsageText = replacedMiniDescriptionText.replace(readmeUsageRegex, `plugin.type: "${camelizedName}`),
120
+ sampleImageRegex =
121
+ /!\[demo]\(https:\/\/raw.githubusercontent.com\/tsparticles\/plugin-template\/main\/images\/sample.png\)/g,
122
+ repoPath = repoUrl.includes("github.com")
123
+ ? repoUrl.substring(repoUrl.indexOf("github.com/") + 11, repoUrl.indexOf(".git"))
124
+ : "tsparticles/plugin-template",
125
+ replacedText = replacedUsageText.replace(
126
+ sampleImageRegex,
127
+ `![demo](https://raw.githubusercontent.com/${repoPath}/main/images/sample.png)`
128
+ );
129
+
130
+ await fs.writeFile(readmePath, replacedText);
131
+ }
132
+
133
+ /**
134
+ * Updates the plugin webpack file
135
+ * @param destPath - The path where the project is located
136
+ * @param name - The name of the project
137
+ * @param description - The description of the project
138
+ */
139
+ async function updatePluginWebpackFile(destPath: string, name: string, description: string): Promise<void> {
140
+ await updateWebpackFile(
141
+ destPath,
142
+ camelize(capitalize(capitalize(name, "-"), " ")),
143
+ `tsParticles ${description} Plugin`,
144
+ "loadParticlesPlugin"
145
+ );
146
+ }
147
+
148
+ /**
149
+ * Creates the plugin project
150
+ * @param name - The name of the project
151
+ * @param description - The description of the project
152
+ * @param repoUrl - The repository url
153
+ * @param destPath - The path where the project is located
154
+ */
155
+ export async function createPluginTemplate(
156
+ name: string,
157
+ description: string,
158
+ repoUrl: string,
159
+ destPath: string
160
+ ): Promise<void> {
161
+ const sourcePath = path.resolve(__dirname, "..", "..", "..", "files", "create-plugin");
162
+
163
+ await copyEmptyTemplateFiles(destPath);
164
+
165
+ await fs.copy(sourcePath, destPath, {
166
+ overwrite: true,
167
+ filter: copyFilter,
168
+ });
169
+
170
+ await updateIndexFile(destPath, name);
171
+ await updatePluginPackageFile(destPath, name, description, repoUrl);
172
+ await updatePluginPackageDistFile(destPath, name, description, repoUrl);
173
+ await updateReadmeFile(destPath, name, description, repoUrl);
174
+ await updatePluginWebpackFile(destPath, name, description);
175
+
176
+ runInstall(destPath);
177
+ runBuild(destPath);
178
+ }
@@ -0,0 +1,65 @@
1
+ import prompts, { type PromptObject } from "prompts";
2
+ import { Command } from "commander";
3
+ import { capitalize } from "../../utils/string-utils";
4
+ import { createPluginTemplate } from "./create-plugin";
5
+ import { execSync } from "child_process";
6
+ import fs from "fs-extra";
7
+ import path from "path";
8
+
9
+ const pluginCommand = new Command("plugin");
10
+
11
+ pluginCommand.description("Create a new tsParticles plugin");
12
+ pluginCommand.argument("<destination>", "Destination folder");
13
+ pluginCommand.action(async (destination: string) => {
14
+ let repoUrl: string;
15
+
16
+ const destPath = path.resolve(path.join(process.cwd(), destination)),
17
+ destExists = await fs.pathExists(destPath);
18
+
19
+ if (destExists) {
20
+ const destContents = await fs.readdir(destPath),
21
+ destContentsNoGit = destContents.filter(t => t !== ".git" && t !== ".gitignore");
22
+
23
+ if (destContentsNoGit.length) {
24
+ throw new Error("Destination folder already exists and is not empty");
25
+ }
26
+ }
27
+
28
+ await fs.ensureDir(destPath);
29
+
30
+ try {
31
+ repoUrl = execSync("git config --get remote.origin.url").toString();
32
+ } catch {
33
+ repoUrl = "";
34
+ }
35
+
36
+ const initialName = destPath.split(path.sep).pop(),
37
+ questions: PromptObject[] = [
38
+ {
39
+ type: "text",
40
+ name: "name",
41
+ message: "What is the name of the plugin?",
42
+ validate: (value: string) => (value ? true : "The name can't be empty"),
43
+ initial: initialName,
44
+ },
45
+ {
46
+ type: "text",
47
+ name: "description",
48
+ message: "What is the description of the plugin?",
49
+ validate: (value: string) => (value ? true : "The description can't be empty"),
50
+ initial: capitalize(initialName || ""),
51
+ },
52
+ {
53
+ type: "text",
54
+ name: "repositoryUrl",
55
+ message: "What is the repository URL? (optional)",
56
+ initial: repoUrl.trim(),
57
+ },
58
+ ];
59
+
60
+ const { name, description, repositoryUrl } = await prompts(questions);
61
+
62
+ createPluginTemplate(name.trim(), description.trim(), repositoryUrl.trim(), destPath);
63
+ });
64
+
65
+ export { pluginCommand };
@@ -0,0 +1,175 @@
1
+ import { camelize, capitalize, dash } from "../../utils/string-utils";
2
+ import {
3
+ copyEmptyTemplateFiles,
4
+ copyFilter,
5
+ runBuild,
6
+ runInstall,
7
+ updatePackageDistFile,
8
+ updatePackageFile,
9
+ updateWebpackFile,
10
+ } from "../../utils/template-utils";
11
+ import fs from "fs-extra";
12
+ import path from "path";
13
+
14
+ /**
15
+ * Updates the index file with the correct function name
16
+ * @param destPath - The path where the project is located
17
+ * @param name - The name of the project
18
+ */
19
+ async function updateIndexFile(destPath: string, name: string): Promise<void> {
20
+ const indexPath = path.resolve(destPath, "src", "index.ts"),
21
+ index = await fs.readFile(indexPath, "utf-8"),
22
+ capitalizedName = capitalize(capitalize(name, "-"), " "),
23
+ camelizedName = camelize(capitalizedName),
24
+ indexFunctionRegex = /loadTemplateShape/g,
25
+ replacedFuncText = index.replace(indexFunctionRegex, `load${capitalizedName}Shape`),
26
+ indexNameRegex = /"#template#"/g,
27
+ replacedNameText = replacedFuncText.replace(indexNameRegex, `"${camelizedName}"`);
28
+
29
+ await fs.writeFile(indexPath, replacedNameText);
30
+ }
31
+
32
+ /**
33
+ * Updates the shape package file
34
+ * @param destPath - The path where the project is located
35
+ * @param name - The name of the project
36
+ * @param description - The description of the project
37
+ * @param repoUrl - The repository url
38
+ */
39
+ async function updateShapePackageFile(
40
+ destPath: string,
41
+ name: string,
42
+ description: string,
43
+ repoUrl: string
44
+ ): Promise<void> {
45
+ const camelizedName = camelize(camelize(name, "-"), " "),
46
+ dashedName = dash(camelizedName);
47
+
48
+ updatePackageFile(
49
+ destPath,
50
+ `"tsparticles-shape-${dashedName}"`,
51
+ description,
52
+ `"tsparticles.shape.${camelizedName}.min.js"`,
53
+ repoUrl
54
+ );
55
+ }
56
+
57
+ /**
58
+ * Updates the shape package dist file
59
+ * @param destPath - The path where the project is located
60
+ * @param name - The name of the project
61
+ * @param description - The description of the project
62
+ * @param repoUrl - The repository url
63
+ */
64
+ async function updateShapePackageDistFile(
65
+ destPath: string,
66
+ name: string,
67
+ description: string,
68
+ repoUrl: string
69
+ ): Promise<void> {
70
+ const camelizedName = camelize(camelize(name, "-"), " "),
71
+ dashedName = dash(camelizedName);
72
+
73
+ updatePackageDistFile(
74
+ destPath,
75
+ `"tsparticles-shape-${dashedName}"`,
76
+ description,
77
+ `"tsparticles.shape.${camelizedName}.min.js"`,
78
+ repoUrl
79
+ );
80
+ }
81
+
82
+ /**
83
+ * Updates the shape readme file
84
+ * @param destPath - The path where the project is located
85
+ * @param name - The name of the project
86
+ * @param description - The description of the project
87
+ * @param repoUrl - The repository url
88
+ */
89
+ async function updateReadmeFile(destPath: string, name: string, description: string, repoUrl: string): Promise<void> {
90
+ const readmePath = path.resolve(destPath, "README.md"),
91
+ readme = await fs.readFile(readmePath, "utf-8"),
92
+ capitalizedName = capitalize(capitalize(name, "-"), " "),
93
+ camelizedName = camelize(capitalizedName),
94
+ dashedName = dash(camelizedName),
95
+ readmeDescriptionRegex = /tsParticles Template Shape/g,
96
+ replacedDescriptionText = readme.replace(readmeDescriptionRegex, `tsParticles ${description} Shape`),
97
+ readmePackageNameRegex = /tsparticles-shape-template/g,
98
+ replacedPackageNameText = replacedDescriptionText.replace(
99
+ readmePackageNameRegex,
100
+ `tsparticles-shape-${dashedName}`
101
+ ),
102
+ readmeFileNameRegex = /tsparticles\.shape\.template(\.bundle)?\.min\.js/g,
103
+ replacedFileNameText = replacedPackageNameText.replace(
104
+ readmeFileNameRegex,
105
+ `tsparticles.shape.${camelizedName}$1.min.js`
106
+ ),
107
+ readmeFunctionNameRegex = /loadTemplateShape/g,
108
+ replacedFunctionNameText = replacedFileNameText.replace(readmeFunctionNameRegex, `load${capitalizedName}Shape`),
109
+ readmeMiniDescriptionRegex =
110
+ /\[tsParticles]\(https:\/\/github.com\/matteobruni\/tsparticles\) additional template shape\./g,
111
+ replacedMiniDescriptionText = replacedFunctionNameText.replace(
112
+ readmeMiniDescriptionRegex,
113
+ `[tsParticles](https://github.com/matteobruni/tsparticles) additional ${name} shape.`
114
+ ),
115
+ readmeUsageRegex = /shape\.type: "template"/g,
116
+ replacedUsageText = replacedMiniDescriptionText.replace(readmeUsageRegex, `shape.type: "${camelizedName}`),
117
+ sampleImageRegex =
118
+ /!\[demo]\(https:\/\/raw.githubusercontent.com\/tsparticles\/shape-template\/main\/images\/sample.png\)/g,
119
+ repoPath = repoUrl.includes("github.com")
120
+ ? repoUrl.substring(repoUrl.indexOf("github.com/") + 11, repoUrl.indexOf(".git"))
121
+ : "tsparticles/shape-template",
122
+ replacedText = replacedUsageText.replace(
123
+ sampleImageRegex,
124
+ `![demo](https://raw.githubusercontent.com/${repoPath}/main/images/sample.png)`
125
+ );
126
+
127
+ await fs.writeFile(readmePath, replacedText);
128
+ }
129
+
130
+ /**
131
+ * Updates the shape webpack file
132
+ * @param destPath - The path where the project is located
133
+ * @param name - The name of the project
134
+ * @param description - The description of the project
135
+ */
136
+ async function updateShapeWebpackFile(destPath: string, name: string, description: string): Promise<void> {
137
+ await updateWebpackFile(
138
+ destPath,
139
+ camelize(capitalize(capitalize(name, "-"), " ")),
140
+ `tsParticles ${description} Shape`,
141
+ "loadParticlesShape"
142
+ );
143
+ }
144
+
145
+ /**
146
+ * Creates the shape project
147
+ * @param name - The name of the project
148
+ * @param description - The description of the project
149
+ * @param repoUrl - The repository url
150
+ * @param destPath - The path where the project is located
151
+ */
152
+ export async function createShapeTemplate(
153
+ name: string,
154
+ description: string,
155
+ repoUrl: string,
156
+ destPath: string
157
+ ): Promise<void> {
158
+ const sourcePath = path.resolve(__dirname, "..", "..", "..", "files", "create-shape");
159
+
160
+ await copyEmptyTemplateFiles(destPath);
161
+
162
+ await fs.copy(sourcePath, destPath, {
163
+ overwrite: true,
164
+ filter: copyFilter,
165
+ });
166
+
167
+ await updateIndexFile(destPath, name);
168
+ await updateShapePackageFile(destPath, name, description, repoUrl);
169
+ await updateShapePackageDistFile(destPath, name, description, repoUrl);
170
+ await updateReadmeFile(destPath, name, description, repoUrl);
171
+ await updateShapeWebpackFile(destPath, name, description);
172
+
173
+ runInstall(destPath);
174
+ runBuild(destPath);
175
+ }
@@ -0,0 +1,65 @@
1
+ import prompts, { type PromptObject } from "prompts";
2
+ import { Command } from "commander";
3
+ import { capitalize } from "../../utils/string-utils";
4
+ import { createShapeTemplate } from "./create-shape";
5
+ import { execSync } from "child_process";
6
+ import fs from "fs-extra";
7
+ import path from "path";
8
+
9
+ const shapeCommand = new Command("shape");
10
+
11
+ shapeCommand.description("Create a new tsParticles shape");
12
+ shapeCommand.argument("<destination>", "Destination folder");
13
+ shapeCommand.action(async (destination: string) => {
14
+ let repoUrl: string;
15
+
16
+ const destPath = path.resolve(path.join(process.cwd(), destination)),
17
+ destExists = await fs.pathExists(destPath);
18
+
19
+ if (destExists) {
20
+ const destContents = await fs.readdir(destPath),
21
+ destContentsNoGit = destContents.filter(t => t !== ".git" && t !== ".gitignore");
22
+
23
+ if (destContentsNoGit.length) {
24
+ throw new Error("Destination folder already exists and is not empty");
25
+ }
26
+ }
27
+
28
+ await fs.ensureDir(destPath);
29
+
30
+ try {
31
+ repoUrl = execSync("git config --get remote.origin.url").toString();
32
+ } catch {
33
+ repoUrl = "";
34
+ }
35
+
36
+ const initialName = destPath.split(path.sep).pop(),
37
+ questions: PromptObject[] = [
38
+ {
39
+ type: "text",
40
+ name: "name",
41
+ message: "What is the name of the shape?",
42
+ validate: (value: string) => (value ? true : "The name can't be empty"),
43
+ initial: initialName,
44
+ },
45
+ {
46
+ type: "text",
47
+ name: "description",
48
+ message: "What is the description of the shape?",
49
+ validate: (value: string) => (value ? true : "The description can't be empty"),
50
+ initial: capitalize(initialName || ""),
51
+ },
52
+ {
53
+ type: "text",
54
+ name: "repositoryUrl",
55
+ message: "What is the repository URL? (optional)",
56
+ initial: repoUrl.trim(),
57
+ },
58
+ ];
59
+
60
+ const { name, description, repositoryUrl } = await prompts(questions);
61
+
62
+ createShapeTemplate(name.trim(), description.trim(), repositoryUrl.trim(), destPath);
63
+ });
64
+
65
+ export { shapeCommand };