fragment-tools 0.2.10 → 0.2.11

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/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "fragment-tools",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "description": "A web development environment for creative coding",
5
- "main": "index.js",
5
+ "main": "./src/index.js",
6
6
  "bin": {
7
7
  "fragment": "bin/index.js"
8
8
  },
@@ -30,7 +30,6 @@
30
30
  "convert-length": "^1.0.1",
31
31
  "get-port": "^7.1.0",
32
32
  "gifenc": "^1.0.3",
33
- "glslify": "^7.1.1",
34
33
  "is-unicode-supported": "^2.0.0",
35
34
  "kleur": "^4.1.4",
36
35
  "mediabunny": "^1.13.3",
@@ -1,5 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import fs from 'node:fs';
3
+ import url from 'node:url';
3
4
  import { defineConfig, loadConfigFromFile, mergeConfig } from 'vite';
4
5
  import { svelte } from '@sveltejs/vite-plugin-svelte';
5
6
 
@@ -8,30 +9,47 @@ import { __dirname, file } from './utils.js';
8
9
  import { log } from './log.js';
9
10
  import sketches from './plugins/sketches.js';
10
11
 
12
+ /**
13
+ *
14
+ * @param {{ cwd: string, filepath: string | undefined }} params
15
+ * @returns {Promise<import('../types/config.js').Config>}
16
+ */
11
17
  export async function loadConfig({ cwd, filepath }) {
12
18
  try {
13
- let filename = `fragment.config.js`;
14
- let configFile = filepath ? filepath : filename;
15
19
  let configRoot = cwd;
16
- let resolvedPath = path.resolve(cwd, configFile);
20
+ let filenames = [`fragment.config.js`, `fragment.config.ts`];
17
21
 
18
- if (!fs.existsSync(resolvedPath)) {
19
- if (filepath) {
20
- log.error(`Config file not found: ${resolvedPath}`);
21
- }
22
- return {};
22
+ if (filepath) {
23
+ filenames = [filepath, ...filenames];
23
24
  }
24
25
 
25
- log.info(`Extending configuration from ${resolvedPath}`);
26
+ let filepaths = filenames.map((filename) => {
27
+ return path.resolve(configRoot, filename);
28
+ });
26
29
 
27
- const { config } = await loadConfigFromFile(
28
- {
29
- command: 'build',
30
- mode: 'dev',
31
- },
32
- configFile,
33
- configRoot,
30
+ let resolvedIndex = filepaths.findIndex((filepath) =>
31
+ fs.existsSync(filepath),
34
32
  );
33
+ /** @type {string|undefined} */
34
+ let resolvedPath = filepaths[resolvedIndex];
35
+
36
+ if (filepath && resolvedIndex !== 0) {
37
+ log.error(`Config file not found: ${filepath}`);
38
+ }
39
+
40
+ if (!resolvedPath) {
41
+ return {};
42
+ }
43
+
44
+ let configFile = path.relative(cwd, resolvedPath);
45
+
46
+ log.info(`Extending configuration from ${configFile}`);
47
+
48
+ const config = (
49
+ await import(
50
+ `${url.pathToFileURL(configFile).href}?ts=${Date.now()}`
51
+ )
52
+ ).default;
35
53
 
36
54
  return config;
37
55
  } catch (error) {
@@ -43,9 +61,10 @@ export async function loadConfig({ cwd, filepath }) {
43
61
  /**
44
62
  * Create Vite config from entries
45
63
  * @param {string[]} entries
46
- * @param {options} options
64
+ * @param {object} [options]
47
65
  * @param {boolean} [options.dev=false]
48
66
  * @param {boolean} [options.build=false]
67
+ * @param {string} [configFilepath]
49
68
  * @param {string} [cwd=process.cwd()]
50
69
  * @returns {import('vite').UserConfig}
51
70
  */
@@ -63,8 +82,8 @@ export async function createConfig(
63
82
  log.info(`Creating Vite configuration...`);
64
83
 
65
84
  const config = await loadConfig({
66
- filepath: configFilepath,
67
85
  cwd,
86
+ filepath: configFilepath,
68
87
  });
69
88
 
70
89
  return mergeConfig(
@@ -1,7 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import fs from 'node:fs';
3
3
  import { readFile } from 'node:fs/promises';
4
- import glslify from 'glslify';
5
4
  import { log, dim, green, yellow } from '../log.js';
6
5
 
7
6
  /**
@@ -232,10 +231,6 @@ ${keyword}${shaderParts[1]}
232
231
  shaderPath,
233
232
  );
234
233
 
235
- code = glslify(code, {
236
- basedir: process.cwd(),
237
- });
238
-
239
234
  if (server) {
240
235
  code = addShaderFilepath(code, shaderPath);
241
236
  }
package/src/cli/run.js CHANGED
@@ -26,17 +26,23 @@ import hotShaderReplacement from './plugins/hot-shader-replacement.js';
26
26
  */
27
27
  export async function run(entry, options = {}) {
28
28
  let fragmentServer;
29
+ /** @type {import('node:fs').FSWatcher} */
30
+ let watcher;
29
31
 
30
32
  const cwd = process.cwd();
31
33
  const command = `run`;
32
34
  const prefix = log.prefix(command);
35
+
36
+ const stop = () => {
37
+ fragmentServer?.close();
38
+ watcher?.close();
39
+ };
40
+
33
41
  const exit = () => {
34
42
  process.off('SIGTERM', exit);
35
43
  process.off('exit', exit);
36
44
 
37
- if (fragmentServer) {
38
- fragmentServer.close();
39
- }
45
+ stop();
40
46
 
41
47
  console.log();
42
48
  };
@@ -62,9 +68,10 @@ export async function run(entry, options = {}) {
62
68
  );
63
69
  }
64
70
 
71
+ const hasTSFiles = entries.some((entry) => entry.endsWith('ts'));
65
72
  const tsConfigDirpath = path.join(cwd, FRAGMENT_DIRECTORY);
66
73
  const tsConfigFilepath = path.join(tsConfigDirpath, 'tsconfig.json');
67
- if (!fs.existsSync(tsConfigFilepath)) {
74
+ if (!fs.existsSync(tsConfigFilepath) && hasTSFiles) {
68
75
  await createTsConfigFile(cwd);
69
76
  }
70
77
 
@@ -106,6 +113,22 @@ export async function run(entry, options = {}) {
106
113
  ],
107
114
  }),
108
115
  );
116
+
117
+ watcher = fs.watch(cwd, (eventType, filename) => {
118
+ if (
119
+ ['fragment.config.js', 'fragment.config.ts'].includes(
120
+ filename,
121
+ ) ||
122
+ options.configFilepath?.includes(filename)
123
+ ) {
124
+ log.warn(`${filename} has changed. Restarting...`);
125
+ console.log();
126
+ server.close();
127
+ stop();
128
+ run(entry, options);
129
+ }
130
+ });
131
+
109
132
  await server.listen();
110
133
 
111
134
  // line break after logs
@@ -130,8 +153,6 @@ export async function run(entry, options = {}) {
130
153
 
131
154
  // line break before fragment logs
132
155
  log.message();
133
-
134
- return server;
135
156
  } catch (error) {
136
157
  // line break before error
137
158
  log.message();
@@ -556,7 +556,7 @@ export class Render {
556
556
  }
557
557
 
558
558
  async screenshot({
559
- filename = this.sketch.name ?? this.sketch.key,
559
+ filename = this.sketch.key,
560
560
  pattern = this.sketch.filenamePattern,
561
561
  exportDir = this.sketch.exportDir,
562
562
  } = {}) {
package/src/index.js ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @typedef {import('./types/config').Config} Config
3
+ */
4
+
5
+ /**
6
+ * Type helper to make it easier to use fragment.config.js
7
+ * @param {Config} config
8
+ * @returns {Config}
9
+ */
10
+ export function defineConfig(config = {}) {
11
+ return config;
12
+ }
@@ -0,0 +1,6 @@
1
+ import { UserConfig } from 'vite';
2
+
3
+ export interface Config {
4
+ vite?: UserConfig;
5
+ [key: string]: any;
6
+ }
@@ -1,5 +1,6 @@
1
1
  export type * from './renderers';
2
2
  export type * from './sketch';
3
+ export type * from './config';
3
4
  export type * from './props';
4
5
  export type * from './helpers';
5
6
  export type * from './hooks';
@@ -3,7 +3,7 @@ type BaseProp<Value, Params, Type> = {
3
3
  params?: Params;
4
4
  type?: Type;
5
5
  hidden?: boolean;
6
- displayName?: string;
6
+ displayName?: string | null;
7
7
  folder?: string;
8
8
  group?: string;
9
9
  onChange?: PropOnChange<Value, Params>;
@@ -25,11 +25,37 @@ type NumberProp = BaseProp<
25
25
  { disabled?: boolean; step?: number } | { min: number; max: number },
26
26
  'number'
27
27
  >;
28
- type VecProp = BaseProp<
28
+
29
+ type VecArray =
29
30
  | [number, number]
30
31
  | [number, number, number]
31
- | [number, number, number, number],
32
- { locked?: boolean },
32
+ | [number, number, number, number];
33
+
34
+ type VecObject = Record<string, number> & { [key: number]: never };
35
+
36
+ type VecValue = VecArray | VecObject;
37
+
38
+ type VecArrayParams<V extends VecArray> = {
39
+ min: { [K in keyof V]: number };
40
+ max: { [K in keyof V]: number };
41
+ step?: { [K in keyof V]: number };
42
+ };
43
+
44
+ type VecObjectParams<V extends VecObject> = {
45
+ min: { [K in keyof V]: number };
46
+ max: { [K in keyof V]: number };
47
+ step?: { [K in keyof V]: number };
48
+ };
49
+
50
+ type VecParams<V extends VecValue> = V extends readonly number[]
51
+ ? VecArrayParams<V>
52
+ : V extends Record<string, number>
53
+ ? VecObjectParams<V>
54
+ : never;
55
+
56
+ type VecProp<V extends VecValue = VecValue> = BaseProp<
57
+ V,
58
+ { locked?: boolean } | VecParams<V>,
33
59
  'vec'
34
60
  >;
35
61
  type CheckboxProp = BaseProp<boolean, never, 'checkbox'>;
@@ -50,7 +76,8 @@ type ImageProp = BaseProp<string, never, 'image'>;
50
76
  type Prop =
51
77
  | SelectProp
52
78
  | NumberProp
53
- | VecProp
79
+ | VecProp<VecObject>
80
+ | VecProp<VecArray>
54
81
  | CheckboxProp
55
82
  | TextProp
56
83
  | ListProp