dazscript-framework 0.1.17 → 0.2.2

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.
package/README.md CHANGED
@@ -21,76 +21,36 @@ The **DazScript Framework** is a TypeScript-based framework for writing Daz Stud
21
21
  To install the **DazScript Framework**, run the following command:
22
22
 
23
23
  ```bash
24
- npm install dazscript-framework
24
+ npm install dazscript-framework dazscript-types
25
25
  ```
26
26
 
27
27
  ## Setup
28
28
 
29
- After installing the package, you will need to configure a few files for your project.
29
+ After installing the package, scaffold the project files:
30
30
 
31
- 1. **babel.config.js**
32
-
33
- Create the file and add the following content:
34
-
35
- ```javascript
36
- const sharedBabelConfig = require('dazscript-framework/babel');
37
-
38
- module.exports = {
39
- ...sharedBabelConfig,
40
- presets: [...sharedBabelConfig.presets],
41
- plugins: [...sharedBabelConfig.plugins],
42
- };
43
- ```
44
-
45
- 2. **package.json**
46
-
47
- Add the following scripts to your package.json:
48
-
49
- ```json
50
- "scripts": {
51
- "prebuild": "npm run installer",
52
- "build": "webpack --env outputPath=./out",
53
- "postbuild": "npm run icons",
54
- "watch": "webpack --env outputPath=./out --watch",
55
- "icons": "copyfiles -u 1 src/**/*.png out/",
56
- "installer": "node ./node_modules/dazscript-framework/dist/scripts/install-generator.js -p ./src/scripts -m /MyScripts"
57
- }
58
- ```
31
+ ```bash
32
+ npx dazscript init
33
+ ```
59
34
 
60
- 3. **tsconfig.json**
35
+ This generates:
61
36
 
62
- Create the file and add the following content:
37
+ - `dazscript.config.ts`
38
+ - `tsconfig.json`
39
+ - `package.json` script wiring for `build`, `watch`, `icons`, and `installer`
63
40
 
64
- ```json
65
- {
66
- "extends": "./node_modules/dazscript-framework/tsconfig.json",
67
- "compilerOptions": {
68
- "baseUrl": "./",
69
- "paths": {
70
- "shared/*": ["src/shared/*"],
71
- "@dst/*": ["node_modules/dazscript-types/*"],
72
- "@dsf/*": ["node_modules/dazscript-framework/src/*"]
73
- }
74
- },
75
- "include": ["node_modules/dazscript-types/**/*", "src/**/*"]
76
- }
77
- ```
41
+ The generated package scripts use the framework CLI directly, so consumer projects do not need their own webpack or Babel setup.
78
42
 
79
- 4. **webpack.config.js**
80
- Create the file and add the following content:
43
+ You can customize the generated defaults:
81
44
 
82
- ```javascript
83
- const sharedWebpackConfig = require('dazscript-framework/webpack');
45
+ ```bash
46
+ npx dazscript init --menu-path /MyScripts --scripts-path ./src --out-dir ./out
47
+ ```
84
48
 
85
- module.exports = (env, argv) => {
86
- const sharedConfig = sharedWebpackConfig(env, argv);
49
+ - `--menu-path` sets which Daz Studio menu the scripts are added to by default. See [The `@action` Decorator](#the-action-decorator) for how a script can override that with `menuPath`.
50
+ - `--scripts-path` tells the installer generator where to scan for runnable `.dsa.ts` entry files.
51
+ - `--out-dir` sets the webpack build output directory for generated `.dsa` files and copied icons.
87
52
 
88
- return {
89
- ...sharedConfig,
90
- // You can override or add more customizations here if needed
91
- };
92
- };
93
- ```
53
+ Use `--scripts-path ./src/scripts` for projects shaped like `scripts/common`, where runnable `.dsa.ts` files live under `src/scripts/`. Use `--scripts-path ./src` for packages shaped like `scripts/power-menu`, where runnable `.dsa.ts` files live at the source root.
94
54
 
95
55
  ## Usage
96
56
 
@@ -115,6 +75,36 @@ class HelloWorldScript extends BaseScript {
115
75
  new HelloWorldScript().exec();
116
76
  ```
117
77
 
78
+ ### The `@action` Decorator
79
+
80
+ Use `@action(...)` on a script class to register how it should appear in Daz Studio.
81
+
82
+ ```typescript
83
+ @action({
84
+ text: 'Hello World',
85
+ menuPath: '#{defaultMenuPath}/Examples',
86
+ shortcut: 'CTRL+SHIFT+H',
87
+ toolbar: 'MyToolbar',
88
+ group: 'Examples',
89
+ description: 'Runs the Hello World script',
90
+ })
91
+ class HelloWorldScript extends BaseScript {
92
+ protected run(): void {
93
+ info('Hello World!');
94
+ }
95
+ }
96
+ ```
97
+
98
+ Common `@action(...)` parameters:
99
+
100
+ - `text`: the label shown for the script in Daz Studio.
101
+ - `menuPath`: the menu path where the script should be added. Set it to `false` to skip adding the script to a menu. If omitted, the default menu from `--menu-path` is used.
102
+ - `shortcut`: the keyboard shortcut for the action.
103
+ - `toolbar`: the toolbar name used when the action should appear on a toolbar.
104
+ - `group`: an optional grouping label used by Daz Studio for related actions.
105
+ - `description`: a longer description for the action.
106
+ - `bundle`: generates installer and uninstaller entries as a package bundle instead of a single action entry.
107
+
118
108
  ### Building UIs with Observables & Dialogs
119
109
 
120
110
  The framework uses a **Model-View pattern** with reactive data bindings:
@@ -318,8 +308,7 @@ my-daz-scripts/
318
308
  ├── out/ # Generated .dsa files (build output)
319
309
  ├── package.json
320
310
  ├── tsconfig.json
321
- ├── webpack.config.js
322
- └── babel.config.js
311
+ └── dazscript.config.ts
323
312
  ```
324
313
 
325
314
  **Key points:**
package/babel.config.js CHANGED
@@ -1,7 +1,7 @@
1
1
  module.exports = {
2
2
  presets: [
3
3
  [
4
- '@babel/preset-env',
4
+ require.resolve('@babel/preset-env'),
5
5
  {
6
6
  targets: {
7
7
  esmodules: true,
@@ -10,24 +10,16 @@ module.exports = {
10
10
  include: ['@babel/plugin-transform-class-properties'],
11
11
  },
12
12
  ],
13
- '@babel/preset-typescript',
13
+ require.resolve('@babel/preset-typescript'),
14
14
  ],
15
15
  plugins: [
16
- '@babel/plugin-transform-class-properties',
17
- 'babel-plugin-transform-typescript-metadata',
18
- ['@babel/plugin-proposal-decorators', { version: 'legacy' }],
19
- ['@babel/plugin-transform-arrow-functions'],
20
- '@babel/plugin-transform-block-scoping',
21
- '@babel/plugin-proposal-class-properties',
22
- '@babel/plugin-transform-private-property-in-object',
23
- '@babel/plugin-transform-private-methods',
24
- [
25
- 'dazscript-framework/babel/trace-babel-plugin', // Ensure this path is correct
26
- { default: false, retainLines: false },
27
- ],
28
- [
29
- 'dazscript-framework/babel/trace-log-babel-plugin', // Ensure this path is correct
30
- { default: false, retainLines: false },
31
- ],
16
+ require.resolve('@babel/plugin-transform-class-properties'),
17
+ require.resolve('babel-plugin-transform-typescript-metadata'),
18
+ [require.resolve('@babel/plugin-proposal-decorators'), { version: 'legacy' }],
19
+ [require.resolve('@babel/plugin-transform-arrow-functions')],
20
+ require.resolve('@babel/plugin-transform-block-scoping'),
21
+ require.resolve('@babel/plugin-proposal-class-properties'),
22
+ require.resolve('@babel/plugin-transform-private-property-in-object'),
23
+ require.resolve('@babel/plugin-transform-private-methods'),
32
24
  ],
33
25
  };
package/dist/config.js ADDED
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ function defineConfig(config) {
4
+ return config;
5
+ }
6
+
7
+ module.exports = {
8
+ defineConfig,
9
+ };
@@ -0,0 +1,82 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const webpack = require('webpack');
5
+ const createWebpackConfig = require('../../webpack.config.js');
6
+
7
+ function runWebpack(workdir, options) {
8
+ const env = {
9
+ context: workdir,
10
+ outputPath: options.outDir || './out',
11
+ };
12
+
13
+ if (options.file) {
14
+ env.file = options.file;
15
+ }
16
+
17
+ const config = createWebpackConfig(env, {
18
+ mode: options.watch ? 'development' : 'production',
19
+ });
20
+
21
+ return new Promise((resolve, reject) => {
22
+ const compiler = webpack(config);
23
+
24
+ const handleStats = (error, stats) => {
25
+ if (error) {
26
+ reject(error);
27
+ return;
28
+ }
29
+
30
+ if (stats.hasErrors()) {
31
+ reject(new Error(stats.toString({ colors: true })));
32
+ return;
33
+ }
34
+
35
+ console.log(
36
+ stats.toString({
37
+ colors: true,
38
+ chunks: false,
39
+ modules: false,
40
+ })
41
+ );
42
+
43
+ if (!options.watch) {
44
+ resolve();
45
+ }
46
+ };
47
+
48
+ if (options.watch) {
49
+ compiler.watch({}, handleStats);
50
+ return;
51
+ }
52
+
53
+ compiler.run((error, stats) => {
54
+ if (error || stats.hasErrors()) {
55
+ handleStats(error, stats);
56
+ compiler.close(() => {});
57
+ return;
58
+ }
59
+
60
+ console.log(
61
+ stats.toString({
62
+ colors: true,
63
+ chunks: false,
64
+ modules: false,
65
+ })
66
+ );
67
+
68
+ compiler.close((closeError) => {
69
+ if (closeError) {
70
+ reject(closeError);
71
+ return;
72
+ }
73
+
74
+ resolve();
75
+ });
76
+ });
77
+ });
78
+ }
79
+
80
+ module.exports = {
81
+ runWebpack,
82
+ };
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { runWebpack } = require('./build');
5
+ const { loadConfig } = require('./config-loader');
6
+ const { copyIcons } = require('./icons');
7
+ const { generateInstallerFiles } = require('./install-generator');
8
+ const { initProject } = require('./init');
9
+
10
+ function printHelp() {
11
+ console.log(`dazscript <command> [options]
12
+
13
+ Commands:
14
+ init Scaffold a DazScript project in the current directory
15
+ build Build DazScript files
16
+ watch Build and watch DazScript files
17
+ icons Copy png assets into the output directory
18
+ installer Generate Install.dsa.ts and Uninstall.dsa.ts
19
+
20
+ Options for init:
21
+ --menu-path <path> Default menu path. Default: /MyScripts
22
+ --scripts-path <path> Source directory to scan. Default: ./src
23
+ --out-dir <path> Build output directory. Default: ./out
24
+ --force Overwrite generated files
25
+ --help Show this message
26
+ `);
27
+ }
28
+
29
+ function parseOptions(args, defaults) {
30
+ const options = { ...defaults };
31
+
32
+ for (let index = 0; index < args.length; index += 1) {
33
+ const arg = args[index];
34
+
35
+ if (arg === '--force') {
36
+ options.force = true;
37
+ continue;
38
+ }
39
+
40
+ if (arg === '--menu-path') {
41
+ options.menuPath = args[index + 1];
42
+ index += 1;
43
+ continue;
44
+ }
45
+
46
+ if (arg === '--scripts-path') {
47
+ options.scriptsPath = args[index + 1];
48
+ index += 1;
49
+ continue;
50
+ }
51
+
52
+ if (arg === '--out-dir') {
53
+ options.outDir = args[index + 1];
54
+ index += 1;
55
+ continue;
56
+ }
57
+
58
+ if (arg === '--file') {
59
+ options.file = args[index + 1];
60
+ index += 1;
61
+ continue;
62
+ }
63
+
64
+ if (arg === '--help') {
65
+ options.help = true;
66
+ continue;
67
+ }
68
+
69
+ throw new Error(`Unknown option: ${arg}`);
70
+ }
71
+
72
+ return options;
73
+ }
74
+
75
+ function getResolvedOptions(workdir, cliOptions) {
76
+ const { config } = loadConfig(workdir);
77
+
78
+ const options = {
79
+ ...config,
80
+ ...cliOptions,
81
+ };
82
+
83
+ if (!options.menuPath && options.defaultMenuPath) {
84
+ options.menuPath = options.defaultMenuPath;
85
+ }
86
+
87
+ return options;
88
+ }
89
+
90
+ async function main(argv) {
91
+ const [, , command, ...rest] = argv;
92
+ const workdir = process.cwd();
93
+
94
+ if (!command || command === '--help' || command === '-h') {
95
+ printHelp();
96
+ return;
97
+ }
98
+
99
+ const options = parseOptions(rest, {
100
+ force: false,
101
+ menuPath: undefined,
102
+ scriptsPath: undefined,
103
+ outDir: undefined,
104
+ file: undefined,
105
+ });
106
+
107
+ if (options.help) {
108
+ printHelp();
109
+ return;
110
+ }
111
+
112
+ const resolvedOptions = getResolvedOptions(workdir, options);
113
+ resolvedOptions.menuPath = resolvedOptions.menuPath || '/MyScripts';
114
+ resolvedOptions.scriptsPath = resolvedOptions.scriptsPath || './src';
115
+ resolvedOptions.outDir = resolvedOptions.outDir || './out';
116
+
117
+ if (command === 'init') {
118
+ initProject(workdir, resolvedOptions);
119
+ return;
120
+ }
121
+
122
+ if (command === 'build') {
123
+ await runWebpack(workdir, resolvedOptions);
124
+ return;
125
+ }
126
+
127
+ if (command === 'watch') {
128
+ await runWebpack(workdir, { ...resolvedOptions, watch: true });
129
+ return;
130
+ }
131
+
132
+ if (command === 'icons') {
133
+ copyIcons(workdir, resolvedOptions);
134
+ return;
135
+ }
136
+
137
+ if (command === 'installer') {
138
+ generateInstallerFiles(workdir, {
139
+ scriptsPath: resolvedOptions.scriptsPath,
140
+ defaultMenuPath: resolvedOptions.menuPath,
141
+ });
142
+ return;
143
+ }
144
+
145
+ throw new Error(`Unknown command: ${command}`);
146
+ }
147
+
148
+ try {
149
+ Promise.resolve(main(process.argv)).catch((error) => {
150
+ console.error(error.message);
151
+ process.exitCode = 1;
152
+ });
153
+ } catch (error) {
154
+ console.error(error.message);
155
+ process.exitCode = 1;
156
+ }
@@ -0,0 +1,83 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const ts = require('typescript');
6
+
7
+ function loadModuleFromString(code, filename) {
8
+ const module = { exports: {} };
9
+ const dirname = path.dirname(filename);
10
+ const localRequire = (request) => {
11
+ if (request.startsWith('.')) {
12
+ return require(path.resolve(dirname, request));
13
+ }
14
+
15
+ return require(request);
16
+ };
17
+
18
+ const fn = new Function(
19
+ 'exports',
20
+ 'require',
21
+ 'module',
22
+ '__filename',
23
+ '__dirname',
24
+ code
25
+ );
26
+
27
+ fn(module.exports, localRequire, module, filename, dirname);
28
+ return module.exports;
29
+ }
30
+
31
+ function getConfigCandidates(workdir) {
32
+ return [
33
+ path.join(workdir, 'dazscript.config.ts'),
34
+ path.join(workdir, 'dazscript.config.js'),
35
+ path.join(workdir, 'dazscript.config.cjs'),
36
+ ];
37
+ }
38
+
39
+ function loadTsConfig(filePath) {
40
+ const source = fs.readFileSync(filePath, 'utf8');
41
+ const transpiled = ts.transpileModule(source, {
42
+ compilerOptions: {
43
+ module: ts.ModuleKind.CommonJS,
44
+ target: ts.ScriptTarget.ES2019,
45
+ esModuleInterop: true,
46
+ },
47
+ fileName: filePath,
48
+ });
49
+
50
+ const loaded = loadModuleFromString(transpiled.outputText, filePath);
51
+ return loaded.default || loaded;
52
+ }
53
+
54
+ function loadJsConfig(filePath) {
55
+ const loaded = require(filePath);
56
+ return loaded.default || loaded;
57
+ }
58
+
59
+ function loadConfig(workdir) {
60
+ for (const candidate of getConfigCandidates(workdir)) {
61
+ if (!fs.existsSync(candidate)) {
62
+ continue;
63
+ }
64
+
65
+ const config = candidate.endsWith('.ts')
66
+ ? loadTsConfig(candidate)
67
+ : loadJsConfig(candidate);
68
+
69
+ return {
70
+ config,
71
+ filePath: candidate,
72
+ };
73
+ }
74
+
75
+ return {
76
+ config: {},
77
+ filePath: null,
78
+ };
79
+ }
80
+
81
+ module.exports = {
82
+ loadConfig,
83
+ };
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const glob = require('glob');
6
+
7
+ function copyFile(sourcePath, targetPath) {
8
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
9
+ fs.copyFileSync(sourcePath, targetPath);
10
+ console.log(`copy ${path.relative(process.cwd(), targetPath)}`);
11
+ }
12
+
13
+ function copyIcons(workdir, options) {
14
+ const sourceRoot = path.resolve(workdir, 'src');
15
+ const outDir = path.resolve(workdir, options.outDir || './out');
16
+ const pattern = path.join(sourceRoot, '**/*.png').replace(/\\/g, '/');
17
+ const files = glob.sync(pattern);
18
+
19
+ files.forEach((filePath) => {
20
+ const relativePath = path.relative(sourceRoot, filePath);
21
+ copyFile(filePath, path.join(outDir, relativePath));
22
+ });
23
+ }
24
+
25
+ module.exports = {
26
+ copyIcons,
27
+ };
@@ -0,0 +1,120 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ function normalizePath(value, fallback) {
7
+ if (!value) {
8
+ return fallback;
9
+ }
10
+
11
+ return value.replace(/\\/g, '/');
12
+ }
13
+
14
+ function formatConfigPath(value) {
15
+ if (value.startsWith('./') || value.startsWith('../') || value.startsWith('/')) {
16
+ return value;
17
+ }
18
+
19
+ return `./${value}`;
20
+ }
21
+
22
+ function ensureTrailingSlash(value) {
23
+ return value.endsWith('/') ? value : `${value}/`;
24
+ }
25
+
26
+ function readJson(filePath) {
27
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
28
+ }
29
+
30
+ function writeJson(filePath, json) {
31
+ fs.writeFileSync(filePath, `${JSON.stringify(json, null, 2)}\n`);
32
+ }
33
+
34
+ function writeFileIfNeeded(filePath, content, force) {
35
+ if (fs.existsSync(filePath) && !force) {
36
+ console.log(`skip ${path.basename(filePath)}`);
37
+ return false;
38
+ }
39
+
40
+ fs.writeFileSync(filePath, content);
41
+ console.log(`write ${path.basename(filePath)}`);
42
+ return true;
43
+ }
44
+
45
+ function buildConfigContent(options) {
46
+ return `import { defineConfig } from 'dazscript-framework/config';
47
+
48
+ export default defineConfig({
49
+ scriptsPath: '${options.scriptsPath}',
50
+ outDir: '${options.outDir}',
51
+ defaultMenuPath: '${options.menuPath}',
52
+ });
53
+ `;
54
+ }
55
+
56
+ function buildTsconfigContent() {
57
+ return `{
58
+ "extends": "./node_modules/dazscript-framework/tsconfig.json",
59
+ "compilerOptions": {
60
+ "baseUrl": "./",
61
+ "paths": {
62
+ "shared/*": ["src/shared/*"],
63
+ "@dst/*": ["node_modules/dazscript-types/src/types/*"],
64
+ "@dsf/*": ["node_modules/dazscript-framework/src/*"]
65
+ }
66
+ },
67
+ "include": ["node_modules/dazscript-types/src/types/**/*", "src/**/*"]
68
+ }
69
+ `;
70
+ }
71
+
72
+ function updatePackageJson(workdir, options) {
73
+ const packageJsonPath = path.join(workdir, 'package.json');
74
+ const packageJson = fs.existsSync(packageJsonPath)
75
+ ? readJson(packageJsonPath)
76
+ : { private: true };
77
+
78
+ if (packageJson.sideEffects === undefined) {
79
+ packageJson.sideEffects = false;
80
+ }
81
+
82
+ packageJson.scripts = {
83
+ ...(packageJson.scripts || {}),
84
+ prebuild: 'npm run installer',
85
+ build: 'dazscript build',
86
+ postbuild: 'npm run icons',
87
+ watch: 'dazscript watch',
88
+ icons: 'dazscript icons',
89
+ installer: 'dazscript installer',
90
+ };
91
+
92
+ writeJson(packageJsonPath, packageJson);
93
+ console.log('update package.json');
94
+ }
95
+
96
+ function initProject(workdir, rawOptions) {
97
+ const options = {
98
+ force: Boolean(rawOptions.force),
99
+ menuPath: normalizePath(rawOptions.menuPath, '/MyScripts'),
100
+ scriptsPath: normalizePath(rawOptions.scriptsPath, './src'),
101
+ outDir: normalizePath(rawOptions.outDir, './out'),
102
+ };
103
+
104
+ writeFileIfNeeded(
105
+ path.join(workdir, 'dazscript.config.ts'),
106
+ buildConfigContent(options),
107
+ options.force
108
+ );
109
+ writeFileIfNeeded(
110
+ path.join(workdir, 'tsconfig.json'),
111
+ buildTsconfigContent(),
112
+ options.force
113
+ );
114
+
115
+ updatePackageJson(workdir, options);
116
+ }
117
+
118
+ module.exports = {
119
+ initProject,
120
+ };