fragment-tools 0.1.14 → 0.1.15
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/.prettierignore +1 -2
- package/.prettierrc +23 -7
- package/README.md +28 -9
- package/bin/index.js +69 -9
- package/package.json +11 -2
- package/src/cli/build.js +125 -0
- package/src/cli/create.js +238 -0
- package/src/cli/createConfig.js +82 -0
- package/src/cli/createFragmentFile.js +70 -0
- package/src/cli/getEntries.js +85 -0
- package/src/cli/log.js +38 -36
- package/src/cli/plugins/check-dependencies.js +52 -23
- package/src/cli/plugins/hot-shader-replacement.js +63 -39
- package/src/cli/plugins/hot-sketch-reload.js +21 -15
- package/src/cli/plugins/save.js +101 -0
- package/src/cli/preview.js +55 -0
- package/src/cli/prompts.js +260 -0
- package/src/cli/run.js +131 -0
- package/src/cli/templates/blank/index.js +33 -0
- package/src/cli/templates/blank/meta.json +4 -0
- package/src/cli/templates/default/index.js +39 -0
- package/src/cli/templates/default/meta.json +5 -0
- package/src/cli/templates/fragment-gl/index.js +37 -0
- package/src/cli/templates/fragment-gl/meta.json +4 -0
- package/src/cli/templates/p5/index.js +32 -0
- package/src/cli/templates/p5/meta.json +5 -0
- package/src/cli/templates/p5-webgl/fragment.fs +14 -0
- package/src/cli/templates/p5-webgl/index.js +67 -0
- package/src/cli/templates/p5-webgl/meta.json +5 -0
- package/src/cli/templates/three-fragment/fragment.fs +10 -0
- package/src/cli/templates/three-fragment/index.js +95 -0
- package/src/cli/templates/three-fragment/meta.json +5 -0
- package/src/cli/templates/three-orthographic/index.js +55 -0
- package/src/cli/templates/three-orthographic/meta.json +5 -0
- package/src/cli/templates/three-perspective/index.js +52 -0
- package/src/cli/templates/three-perspective/meta.json +5 -0
- package/src/cli/utils.js +70 -0
- package/src/cli/ws.js +87 -78
- package/src/client/app/components/IconCross.svelte +18 -18
- package/src/client/app/components/Init.svelte +30 -1
- package/src/client/app/components/KeyBinding.svelte +22 -22
- package/src/client/app/inputs/Input.js +9 -9
- package/src/client/app/inputs/Mouse.js +1 -1
- package/src/client/app/inputs/Webcam.js +89 -88
- package/src/client/app/lib/canvas-recorder/FrameRecorder.js +7 -6
- package/src/client/app/lib/canvas-recorder/H264Recorder.js +45 -0
- package/src/client/app/lib/canvas-recorder/MP4Recorder.js +7 -9
- package/src/client/app/lib/canvas-recorder/WebMRecorder.js +3 -4
- package/src/client/app/lib/canvas-recorder/mp4.js +1649 -15
- package/src/client/app/lib/canvas-recorder/utils.js +33 -17
- package/src/client/app/lib/gl/Geometry.js +11 -8
- package/src/client/app/lib/gl/Program.js +38 -19
- package/src/client/app/lib/gl/Renderer.js +124 -105
- package/src/client/app/lib/gl/Texture.js +113 -85
- package/src/client/app/lib/gl/index.js +12 -12
- package/src/client/app/lib/gl/utils.js +1 -3
- package/src/client/app/lib/helpers/frameDebounce.js +30 -30
- package/src/client/app/lib/loader/index.js +10 -10
- package/src/client/app/lib/loader/loadImage.js +15 -15
- package/src/client/app/lib/loader/loadScript.js +1 -1
- package/src/client/app/lib/paper-sizes.js +75 -76
- package/src/client/app/lib/presets.js +25 -5
- package/src/client/app/lib/tempo/Analyser.js +18 -17
- package/src/client/app/lib/tempo/Range.js +15 -12
- package/src/client/app/lib/tempo/index.js +34 -27
- package/src/client/app/modules/AudioAnalyser/Range.svelte +69 -72
- package/src/client/app/modules/AudioAnalyser/Spectrum.svelte +20 -19
- package/src/client/app/modules/AudioAnalyser.svelte +52 -35
- package/src/client/app/modules/Console/ConsoleLine.svelte +193 -172
- package/src/client/app/modules/Console.svelte +76 -74
- package/src/client/app/modules/Monitor.svelte +57 -57
- package/src/client/app/modules/Params.svelte +17 -5
- package/src/client/app/renderers/P5GLRenderer.js +144 -0
- package/src/client/app/stores/audioAnalysis.js +3 -4
- package/src/client/app/stores/console.js +9 -10
- package/src/client/app/stores/errors.js +1 -1
- package/src/client/app/stores/index.js +2 -2
- package/src/client/app/stores/layout.js +143 -138
- package/src/client/app/stores/multisampling.js +4 -4
- package/src/client/app/stores/props.js +52 -12
- package/src/client/app/stores/renderers.js +4 -0
- package/src/client/app/stores/rendering.js +108 -89
- package/src/client/app/stores/time.js +18 -18
- package/src/client/app/transitions/fade.js +3 -3
- package/src/client/app/transitions/index.js +6 -7
- package/src/client/app/transitions/splitX.js +2 -2
- package/src/client/app/transitions/splitY.js +2 -2
- package/src/client/app/triggers/Mouse.js +73 -65
- package/src/client/app/triggers/Trigger.js +59 -58
- package/src/client/app/triggers/index.js +7 -7
- package/src/client/app/triggers/shared.js +5 -5
- package/src/client/app/ui/Build.svelte +70 -71
- package/src/client/app/ui/ErrorOverlay.svelte +118 -104
- package/src/client/app/ui/Field.svelte +58 -26
- package/src/client/app/ui/FieldSection.svelte +5 -3
- package/src/client/app/ui/FieldSpace.svelte +29 -30
- package/src/client/app/ui/FieldTrigger.svelte +256 -244
- package/src/client/app/ui/FieldTriggers.svelte +46 -46
- package/src/client/app/ui/FloatingParams.svelte +29 -30
- package/src/client/app/ui/Layout.svelte +31 -32
- package/src/client/app/ui/LayoutColumn.svelte +4 -4
- package/src/client/app/ui/LayoutComponent.svelte +239 -225
- package/src/client/app/ui/LayoutResizer.svelte +195 -176
- package/src/client/app/ui/LayoutRoot.svelte +6 -6
- package/src/client/app/ui/LayoutRow.svelte +4 -4
- package/src/client/app/ui/LayoutToolbar.svelte +191 -194
- package/src/client/app/ui/Module.svelte +134 -135
- package/src/client/app/ui/ModuleHeaderAction.svelte +81 -78
- package/src/client/app/ui/ModuleHeaderButton.svelte +12 -12
- package/src/client/app/ui/ModuleHeaderSelect.svelte +47 -37
- package/src/client/app/ui/ModuleRenderer.svelte +26 -27
- package/src/client/app/ui/OutputRenderer.svelte +112 -105
- package/src/client/app/ui/ParamsOutput.svelte +28 -11
- package/src/client/app/ui/Preview.svelte +7 -8
- package/src/client/app/ui/SketchRenderer.svelte +35 -16
- package/src/client/app/ui/SketchSelect.svelte +50 -44
- package/src/client/app/ui/fields/CheckboxInput.svelte +1 -1
- package/src/client/app/ui/fields/FieldInputRow.svelte +8 -8
- package/src/client/app/ui/fields/IntervalInput.svelte +268 -0
- package/src/client/app/ui/fields/NumberInput.svelte +10 -11
- package/src/client/app/ui/fields/ProgressInput.svelte +29 -10
- package/src/client/app/ui/fields/TextInput.svelte +10 -11
- package/src/client/app/ui/fields/VectorInput.svelte +2 -2
- package/src/client/app/utils/canvas.utils.js +46 -92
- package/src/client/app/utils/color.utils.js +138 -101
- package/src/client/app/utils/fields.utils.js +131 -0
- package/src/client/app/utils/file.utils.js +169 -1
- package/src/client/app/utils/glsl.utils.js +2 -2
- package/src/client/app/utils/glslErrors.js +49 -31
- package/src/client/app/utils/index.js +32 -29
- package/src/client/app/utils/math.utils.js +14 -10
- package/src/client/index.html +16 -16
- package/src/client/main.js +4 -4
- package/src/client/public/css/global.css +6 -2
- package/src/cli/db.js +0 -17
- package/src/cli/index.js +0 -198
- package/src/cli/plugins/db.js +0 -12
- package/src/cli/plugins/screenshot.js +0 -83
- package/src/cli/server.js +0 -164
- package/src/cli/templates/2d.js +0 -15
- package/src/cli/templates/blank.js +0 -13
- package/src/cli/templates/fragment.js +0 -18
- package/src/cli/templates/index.js +0 -27
- package/src/cli/templates/p5.js +0 -13
- package/src/cli/templates/three-fragment.js +0 -53
- package/src/cli/templates/three-orthographic.js +0 -23
- package/src/cli/templates/three-perspective.js +0 -20
- package/src/client/app/lib/canvas-recorder/FFMPEGRecorder.js +0 -56
- package/src/client/app/utils/props.utils.js +0 -51
- package/src/client/public/fonts/Inter-Bold.woff2 +0 -0
- package/src/client/public/fonts/Inter-Italic.woff2 +0 -0
- package/src/client/public/fonts/Inter-Regular.woff2 +0 -0
- package/src/client/public/fonts/Inter-SemiBold.woff2 +0 -0
- package/src/client/public/js/ffmpeg.min.js +0 -2
- package/src/client/public/js/ffmpeg.min.js.map +0 -1
- /package/src/cli/templates/{fragment.fs → fragment-gl/fragment.fs} +0 -0
package/.prettierignore
CHANGED
package/.prettierrc
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
"semi": true,
|
|
3
|
+
"tabWidth": 4,
|
|
4
|
+
"useTabs": true,
|
|
5
|
+
"singleQuote": true,
|
|
6
|
+
"printWidth": 80,
|
|
7
|
+
"bracketSpacing": true,
|
|
8
|
+
"trailingComma": "all",
|
|
9
|
+
"plugins": ["prettier-plugin-svelte"],
|
|
10
|
+
"overrides": [
|
|
11
|
+
{
|
|
12
|
+
"files": ["README.md", "packages/*/README.md", "**/package.json"],
|
|
13
|
+
"options": {
|
|
14
|
+
"useTabs": false,
|
|
15
|
+
"tabWidth": 2
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"files": ["docs/**/*.md"],
|
|
20
|
+
"options": {
|
|
21
|
+
"printWidth": 60
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
]
|
|
9
25
|
}
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div align="center">A web development environment for creative coding</div>
|
|
3
3
|
<br/>
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|
|
|
7
7
|
`fragment` provides a simple API to work with `<canvas>`.
|
|
8
8
|
|
|
@@ -12,17 +12,36 @@
|
|
|
12
12
|
- Built-in GUI from sketch files
|
|
13
13
|
- Export `<canvas>` to images (.png, .webm, .jpg) or videos (.mp4, .webm, .gif) on the fly
|
|
14
14
|
- Hot shader reloading & [glslify](https://github.com/glslify/glslify) support
|
|
15
|
-
- Interactive sketches using
|
|
15
|
+
- Interactive sketches using _triggers_
|
|
16
16
|
- Static build for production deployment
|
|
17
17
|
|
|
18
18
|
## Installation
|
|
19
19
|
|
|
20
|
+
### With `npx`
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx fragment-tools sketch.js --new
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Installing globally with `--global`
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install fragment-tools --global
|
|
20
30
|
```
|
|
21
|
-
npm install fragment-tools -g
|
|
22
|
-
```
|
|
23
31
|
|
|
24
32
|
You should now be able to run `fragment` from your command line.
|
|
25
33
|
|
|
34
|
+
```bash
|
|
35
|
+
fragment sketch.js --new
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Installing locally
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install fragment-tools
|
|
42
|
+
npx fragment sketch.js --new
|
|
43
|
+
```
|
|
44
|
+
|
|
26
45
|
## Usage
|
|
27
46
|
|
|
28
47
|
```
|
|
@@ -33,7 +52,7 @@ mkdir sketches
|
|
|
33
52
|
cd sketches
|
|
34
53
|
|
|
35
54
|
# create a sketch from a template
|
|
36
|
-
fragment ./sketch.js --new --template=2d
|
|
55
|
+
npx fragment-tools ./sketch.js --new --template=2d
|
|
37
56
|
```
|
|
38
57
|
|
|
39
58
|
Learn more about the available flag options in the [CLI docs](./docs/api/CLI.md).
|
|
@@ -48,9 +67,9 @@ export let props = {
|
|
|
48
67
|
value: 10,
|
|
49
68
|
params: {
|
|
50
69
|
min: 4,
|
|
51
|
-
max: 30
|
|
52
|
-
}
|
|
53
|
-
}
|
|
70
|
+
max: 30,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
54
73
|
};
|
|
55
74
|
|
|
56
75
|
export let update = ({ context, width, height }) => {
|
|
@@ -80,7 +99,7 @@ Feel free to reach out on [Twitter](https://twitter.com/raphaelameaume) if you w
|
|
|
80
99
|
|
|
81
100
|
```
|
|
82
101
|
# clone or fork the project
|
|
83
|
-
git clone https://github.com/raphaelameaume/fragment.git
|
|
102
|
+
git clone https://github.com/raphaelameaume/fragment.git
|
|
84
103
|
|
|
85
104
|
# move to the root of the repository
|
|
86
105
|
cd fragment
|
package/bin/index.js
CHANGED
|
@@ -1,20 +1,80 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import fs from 'node:fs';
|
|
3
4
|
import sade from 'sade';
|
|
4
|
-
import {
|
|
5
|
+
import { grey } from 'kleur/colors';
|
|
6
|
+
import { run } from '../src/cli/run.js';
|
|
7
|
+
import { build } from '../src/cli/build.js';
|
|
8
|
+
import { create } from '../src/cli/create.js';
|
|
9
|
+
import { preview } from '../src/cli/preview.js';
|
|
5
10
|
|
|
6
|
-
|
|
7
|
-
.
|
|
11
|
+
const { version } = JSON.parse(
|
|
12
|
+
fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8'),
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
console.log(`${grey(`[fragment] v${version}`)}\n`);
|
|
16
|
+
|
|
17
|
+
const prog = sade('fragment');
|
|
18
|
+
prog.version(`${version}`);
|
|
19
|
+
|
|
20
|
+
prog.command('run [entry]', '', { default: true })
|
|
8
21
|
.describe('Run a dev environment for fragment')
|
|
9
|
-
.option('-n, --new', 'Create
|
|
22
|
+
.option('-n, --new', 'Create a new sketch', false)
|
|
10
23
|
.option('-t, --template', 'Specify template to create the file from', '2d')
|
|
11
24
|
.option('-p, --port', 'Port to bind', 3000)
|
|
12
25
|
.option('-dev, --development', 'Enable development mode', false)
|
|
13
26
|
.option('-b, --build', 'Build sketch for production', false)
|
|
14
27
|
.option('--exportDir', 'Directory used for exports')
|
|
15
|
-
.option('--outDir', '
|
|
16
|
-
.option('--emptyOutDir', 'Empty outDir before static build'
|
|
28
|
+
.option('--outDir', 'Build output directory')
|
|
29
|
+
.option('--emptyOutDir', 'Empty outDir before static build')
|
|
30
|
+
.option('--base', 'Base public path when served in production', undefined)
|
|
31
|
+
.action((entry, options) => {
|
|
32
|
+
if (options.new) {
|
|
33
|
+
return create(entry, {
|
|
34
|
+
templateName: options.template,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (options.build) {
|
|
39
|
+
return build(entry, {
|
|
40
|
+
development: options.development,
|
|
41
|
+
outDir: options.outDir,
|
|
42
|
+
emptyOutDir: options.emptyOutDir,
|
|
43
|
+
base: options.base,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
run(entry, {
|
|
48
|
+
development: options.development,
|
|
49
|
+
exportDir: options.exportDir,
|
|
50
|
+
port: options.port,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
prog.command('create [entry]')
|
|
55
|
+
.describe('Create a new sketch')
|
|
56
|
+
.option('-t, --template', 'Specify template to create the file from', '2d')
|
|
57
|
+
.action((entry = '', options) => {
|
|
58
|
+
create(entry, {
|
|
59
|
+
templateName: options.template,
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
prog.command('build [entry]')
|
|
64
|
+
.describe('Build a sketch')
|
|
65
|
+
.option('--outDir', 'Output folder', null)
|
|
66
|
+
.option('--emptyOutDir', 'Empty outDir before building for production')
|
|
67
|
+
.option('--base', 'Base public path', undefined)
|
|
68
|
+
.option('-dev, --development', 'Enable development mode', false)
|
|
17
69
|
.action((entry, options) => {
|
|
18
|
-
|
|
19
|
-
})
|
|
20
|
-
|
|
70
|
+
build(entry, options);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
prog.command('preview [directory]')
|
|
74
|
+
.describe('Preview a sketch')
|
|
75
|
+
.option('-o, --open', 'Open in browser', false)
|
|
76
|
+
.action((dir, options) => {
|
|
77
|
+
preview(dir, options);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
prog.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fragment-tools",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "A web development environment for creative coding",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"fragment": "bin/index.js"
|
|
8
8
|
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"format": "prettier . --write"
|
|
11
|
+
},
|
|
9
12
|
"type": "module",
|
|
10
13
|
"repository": {
|
|
11
14
|
"type": "git",
|
|
@@ -18,6 +21,7 @@
|
|
|
18
21
|
},
|
|
19
22
|
"homepage": "https://github.com/raphaelameaume/fragment#readme",
|
|
20
23
|
"dependencies": {
|
|
24
|
+
"@clack/core": "^0.3.3",
|
|
21
25
|
"@sveltejs/vite-plugin-svelte": "^2.4.5",
|
|
22
26
|
"body-parser": "^1.20.0",
|
|
23
27
|
"changedpi": "^1.0.4",
|
|
@@ -26,12 +30,17 @@
|
|
|
26
30
|
"get-port": "^6.0.0",
|
|
27
31
|
"gifenc": "^1.0.3",
|
|
28
32
|
"glslify": "^7.1.1",
|
|
33
|
+
"is-unicode-supported": "^2.0.0",
|
|
29
34
|
"kleur": "^4.1.4",
|
|
30
35
|
"sade": "^1.7.4",
|
|
31
36
|
"svelte": "^4.0.0",
|
|
32
|
-
"svelte-json-tree": "^
|
|
37
|
+
"svelte-json-tree": "^2.2.0",
|
|
33
38
|
"vite": "^4.4.9",
|
|
34
39
|
"webm-writer": "^1.0.0",
|
|
35
40
|
"ws": "^8.2.3"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"prettier": "^3.2.5",
|
|
44
|
+
"prettier-plugin-svelte": "^3.1.2"
|
|
36
45
|
}
|
|
37
46
|
}
|
package/src/cli/build.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { readdir } from 'node:fs/promises';
|
|
3
|
+
import { mergeConfig, build as viteBuild } from 'vite';
|
|
4
|
+
import { createFragmentFile } from './createFragmentFile.js';
|
|
5
|
+
import { createConfig } from './createConfig.js';
|
|
6
|
+
import { getEntries } from './getEntries.js';
|
|
7
|
+
import { log, magenta } from './log.js';
|
|
8
|
+
import * as p from './prompts.js';
|
|
9
|
+
import { handleCancelledPrompt, mkdirp, prettifyTime } from './utils.js';
|
|
10
|
+
import hotShaderReplacement from './plugins/hot-shader-replacement.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Build a sketch for production
|
|
14
|
+
* @param {string} entry
|
|
15
|
+
* @param {object} options
|
|
16
|
+
* @param {string} options.base
|
|
17
|
+
* @param {string} options.outDir
|
|
18
|
+
* @param {boolean} options.emptyOutDir
|
|
19
|
+
* @param {boolean} options.development
|
|
20
|
+
*/
|
|
21
|
+
export async function build(entry, options) {
|
|
22
|
+
const cwd = process.cwd();
|
|
23
|
+
const command = 'build';
|
|
24
|
+
const prefix = log.prefix(command);
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const entries = await getEntries(entry, cwd, command, prefix);
|
|
28
|
+
|
|
29
|
+
if (!entries.length) return;
|
|
30
|
+
|
|
31
|
+
log.message(`${magenta(entry)}\n`, prefix);
|
|
32
|
+
|
|
33
|
+
const outDir = await p.text({
|
|
34
|
+
message: 'Output directory:',
|
|
35
|
+
placeholder: '.',
|
|
36
|
+
hint: '(hit Enter to use current directory)',
|
|
37
|
+
initialValue:
|
|
38
|
+
options.outDir ?? entries[0].split(path.extname(entries[0]))[0],
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
handleCancelledPrompt(outDir, prefix);
|
|
42
|
+
|
|
43
|
+
let outDirPath = path.join(cwd, outDir);
|
|
44
|
+
|
|
45
|
+
// create directory if it doesn't exist
|
|
46
|
+
mkdirp(outDirPath);
|
|
47
|
+
|
|
48
|
+
const files = await readdir(outDirPath);
|
|
49
|
+
|
|
50
|
+
let emptyOutDir = options.emptyOutDir ?? true;
|
|
51
|
+
|
|
52
|
+
if (files.length > 0) {
|
|
53
|
+
log.warn(`${outDirPath} is not an empty folder.\n`);
|
|
54
|
+
|
|
55
|
+
emptyOutDir = await p.confirm({
|
|
56
|
+
message: 'Empty folder before building?',
|
|
57
|
+
active: 'Yes',
|
|
58
|
+
inactive: 'No',
|
|
59
|
+
initialValue: emptyOutDir ?? true,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
handleCancelledPrompt(emptyOutDir, prefix);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const base = await p.text({
|
|
66
|
+
message: 'Base public path:',
|
|
67
|
+
placeholder: `/`,
|
|
68
|
+
hint: '(Hit Enter to validate)',
|
|
69
|
+
initialValue: options.base,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
handleCancelledPrompt(base, prefix);
|
|
73
|
+
|
|
74
|
+
if (entries.length > 0) {
|
|
75
|
+
log.message(
|
|
76
|
+
`Building ${magenta(entries[0])} for production...\n`,
|
|
77
|
+
prefix,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const fragmentFilepath = await createFragmentFile(entries, cwd);
|
|
81
|
+
const config = createConfig(
|
|
82
|
+
entries,
|
|
83
|
+
fragmentFilepath,
|
|
84
|
+
{
|
|
85
|
+
dev: options.development,
|
|
86
|
+
build: true,
|
|
87
|
+
},
|
|
88
|
+
cwd,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (entries.length > 1) {
|
|
92
|
+
log.error(
|
|
93
|
+
`fragment can only build one sketch at a time.`,
|
|
94
|
+
prefix,
|
|
95
|
+
);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let startTime = Date.now();
|
|
100
|
+
|
|
101
|
+
await viteBuild(
|
|
102
|
+
mergeConfig(config, {
|
|
103
|
+
logLevel: 'info',
|
|
104
|
+
base,
|
|
105
|
+
build: {
|
|
106
|
+
outDir: outDirPath,
|
|
107
|
+
emptyOutDir,
|
|
108
|
+
},
|
|
109
|
+
plugins: [hotShaderReplacement({ cwd })],
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// line break after vite logs
|
|
114
|
+
log.message();
|
|
115
|
+
|
|
116
|
+
log.success(
|
|
117
|
+
`Done in ${prettifyTime(Date.now() - startTime)}`,
|
|
118
|
+
prefix,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
log.error(`Error\n`, prefix);
|
|
123
|
+
console.error(error);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { readdir, readFile, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { log, magenta, bold, cyan, dim } from './log.js';
|
|
5
|
+
import * as p from './prompts.js';
|
|
6
|
+
import {
|
|
7
|
+
packageManager,
|
|
8
|
+
file,
|
|
9
|
+
mkdirp,
|
|
10
|
+
handleCancelledPrompt,
|
|
11
|
+
prettifyTime,
|
|
12
|
+
addExtension,
|
|
13
|
+
} from './utils.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create a new sketch
|
|
17
|
+
* @param {string} entry
|
|
18
|
+
* @param {object} options
|
|
19
|
+
* @param {string} options.templateName
|
|
20
|
+
*/
|
|
21
|
+
export async function create(entry, { templateName } = {}) {
|
|
22
|
+
const cwd = process.cwd();
|
|
23
|
+
const prefix = log.prefix('create');
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
log.message(`${magenta(entry)}\n`, prefix);
|
|
27
|
+
|
|
28
|
+
let dir, name;
|
|
29
|
+
|
|
30
|
+
if (entry) {
|
|
31
|
+
const { dir: entryDir, base: entryBase } = path.parse(entry);
|
|
32
|
+
|
|
33
|
+
dir = entryDir;
|
|
34
|
+
name = entryBase;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
dir = await p.text({
|
|
38
|
+
message: 'Specify an output directory:',
|
|
39
|
+
placeholder: '.',
|
|
40
|
+
hint: '(hit Enter to use current directory)',
|
|
41
|
+
initialValue: dir,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
handleCancelledPrompt(dir, prefix);
|
|
45
|
+
|
|
46
|
+
if (!dir) {
|
|
47
|
+
dir = cwd;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
name = await p.text({
|
|
51
|
+
message: 'Specify a sketch name:',
|
|
52
|
+
placeholder: ` `,
|
|
53
|
+
hint: '(hit Enter to validate)',
|
|
54
|
+
initialValue: name,
|
|
55
|
+
validate: (value) => {
|
|
56
|
+
if (value.length === 0) return `A name is required.`;
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
handleCancelledPrompt(name, prefix);
|
|
61
|
+
|
|
62
|
+
name = name.replace(/\s/g, '-');
|
|
63
|
+
|
|
64
|
+
let templatesOptions = fs
|
|
65
|
+
.readdirSync(file('./templates'))
|
|
66
|
+
.map((dir) => {
|
|
67
|
+
const metadata = file(`./templates/${dir}/meta.json`);
|
|
68
|
+
const template = JSON.parse(fs.readFileSync(metadata, 'utf8'));
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
...template,
|
|
72
|
+
path: dir,
|
|
73
|
+
// for prompts
|
|
74
|
+
label: template.name,
|
|
75
|
+
hint: template.description,
|
|
76
|
+
value: template.name,
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// put default template in the first place
|
|
81
|
+
templatesOptions = templatesOptions.sort((a, b) =>
|
|
82
|
+
a.isDefault ? -1 : 1,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
templateName = await p.select({
|
|
86
|
+
message: 'Pick a template:',
|
|
87
|
+
options: templatesOptions,
|
|
88
|
+
initialValue: templatesOptions.find(
|
|
89
|
+
(option) => templateName === option.name,
|
|
90
|
+
)?.value,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
handleCancelledPrompt(templateName, prefix);
|
|
94
|
+
|
|
95
|
+
let template = templatesOptions.find(
|
|
96
|
+
(option) => option.value === templateName,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const hasDependencies = template.dependencies?.length > 0;
|
|
100
|
+
const singleDependency = template.dependencies?.length === 1;
|
|
101
|
+
|
|
102
|
+
if (hasDependencies) {
|
|
103
|
+
const dependenciesList = template.dependencies
|
|
104
|
+
.map((dependency) => bold(`${dependency}`))
|
|
105
|
+
.join(',');
|
|
106
|
+
|
|
107
|
+
log.warn(
|
|
108
|
+
`This template has the following ${singleDependency ? 'dependency' : 'dependencies'}: ${dependenciesList}. ${singleDependency ? 'It' : 'They'} need${singleDependency ? 's' : ''} to be installed before running Fragment.\n`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
mkdirp(dir);
|
|
113
|
+
|
|
114
|
+
const from = file(`./templates/${template.path}`);
|
|
115
|
+
const to = dir;
|
|
116
|
+
|
|
117
|
+
let filename = path.basename(name).split('.')[0];
|
|
118
|
+
let excludes = ['meta.json'];
|
|
119
|
+
let templateFiles = await readdir(from);
|
|
120
|
+
|
|
121
|
+
templateFiles = templateFiles.filter(
|
|
122
|
+
(file) => !excludes.includes(file),
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
let startTime = Date.now();
|
|
126
|
+
log.message(
|
|
127
|
+
`Creating sketch from template ${magenta(template.name)}...\n`,
|
|
128
|
+
prefix,
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const checkForFileExistence = async (filepath) => {
|
|
132
|
+
if (fs.existsSync(filepath)) {
|
|
133
|
+
const dirname = path.dirname(filepath);
|
|
134
|
+
const ext = path.extname(filepath);
|
|
135
|
+
|
|
136
|
+
let filename = path.basename(filepath);
|
|
137
|
+
|
|
138
|
+
log.warn(`${filename} already exists in ${dirname}.\n`);
|
|
139
|
+
|
|
140
|
+
let override = await p.confirm({
|
|
141
|
+
message: `Override ${filename}?`,
|
|
142
|
+
active: 'Yes',
|
|
143
|
+
inactive: 'No',
|
|
144
|
+
initialValue: false,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
handleCancelledPrompt(override, prefix);
|
|
148
|
+
|
|
149
|
+
if (!override) {
|
|
150
|
+
filename = await p.text({
|
|
151
|
+
message: 'Pick a different name:',
|
|
152
|
+
placeholder: ` `,
|
|
153
|
+
hint: '(hit Enter to validate)',
|
|
154
|
+
initialValue: filename,
|
|
155
|
+
validate: (value) => {
|
|
156
|
+
if (value.length === 0)
|
|
157
|
+
return `A name is required.`;
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
handleCancelledPrompt(filename, prefix);
|
|
162
|
+
|
|
163
|
+
filename = filename.replace(/\s/g, '-');
|
|
164
|
+
|
|
165
|
+
return await checkForFileExistence(
|
|
166
|
+
path.join(dirname, addExtension(filename, ext)),
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return filepath;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return filepath;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const newFiles = [];
|
|
177
|
+
|
|
178
|
+
for (let i = 0; i < templateFiles.length; i++) {
|
|
179
|
+
const file = templateFiles[i];
|
|
180
|
+
const source = path.join(from, file);
|
|
181
|
+
const ext = path.extname(source);
|
|
182
|
+
|
|
183
|
+
let dest = path.join(to, `${filename}${ext}`);
|
|
184
|
+
|
|
185
|
+
log.info(
|
|
186
|
+
`Copying templates/${template.path}/${file} to ${path.relative(cwd, dest)}...`,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
dest = await checkForFileExistence(dest);
|
|
190
|
+
|
|
191
|
+
let buffer = await readFile(source);
|
|
192
|
+
let content = buffer.toString();
|
|
193
|
+
|
|
194
|
+
// replace references to template files by new files paths
|
|
195
|
+
templateFiles.forEach((templateFile, index) => {
|
|
196
|
+
if (index === i) return;
|
|
197
|
+
|
|
198
|
+
if (newFiles.length > 0) {
|
|
199
|
+
const newFile = newFiles[index];
|
|
200
|
+
|
|
201
|
+
content = content.replace(
|
|
202
|
+
new RegExp(templateFile, 'g'),
|
|
203
|
+
`${path.basename(newFile)}`,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
await writeFile(dest, Buffer.from(content));
|
|
209
|
+
|
|
210
|
+
log.success(
|
|
211
|
+
`Copied templates/${template.path}/${file} to ${path.relative(cwd, dest)}\n`,
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
newFiles.push(dest);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
log.success(
|
|
218
|
+
`Done in ${prettifyTime(Date.now() - startTime)}\n`,
|
|
219
|
+
prefix,
|
|
220
|
+
);
|
|
221
|
+
log.info(`${newFiles.join('\n')}\n`);
|
|
222
|
+
|
|
223
|
+
let i = 1;
|
|
224
|
+
|
|
225
|
+
let nextSteps = ``;
|
|
226
|
+
|
|
227
|
+
if (hasDependencies) {
|
|
228
|
+
nextSteps += `${dim(`${i++}. Install dependencies`)}\n${bold(cyan(`${packageManager} install ${template.dependencies.join(' ')}`))}\n\n`;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
nextSteps += `${dim(`${i++}. Start Fragment`)}\n${bold(cyan(`fragment ${path.relative(cwd, newFiles[newFiles.length - 1])}`))}`;
|
|
232
|
+
|
|
233
|
+
p.note(nextSteps, 'Next steps');
|
|
234
|
+
} catch (error) {
|
|
235
|
+
log.error(`Error\n`, prefix);
|
|
236
|
+
console.error(error);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { defineConfig } from 'vite';
|
|
3
|
+
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
|
4
|
+
|
|
5
|
+
import checkDependencies from './plugins/check-dependencies.js';
|
|
6
|
+
import { file } from './utils.js';
|
|
7
|
+
import { log } from './log.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Create Vite config from entries
|
|
11
|
+
* @param {string[]} entries
|
|
12
|
+
* @param {string} fragmentFilepath
|
|
13
|
+
* @param {options} options
|
|
14
|
+
* @param {boolean} [options.dev=false]
|
|
15
|
+
* @param {boolean} [options.build=false]
|
|
16
|
+
* @param {string} [cwd=process.cwd()]
|
|
17
|
+
* @returns {import('vite').UserConfig}
|
|
18
|
+
*/
|
|
19
|
+
export function createConfig(
|
|
20
|
+
entries,
|
|
21
|
+
fragmentFilepath,
|
|
22
|
+
{ dev = false, build = false } = {},
|
|
23
|
+
cwd = process.cwd(),
|
|
24
|
+
) {
|
|
25
|
+
const entriesPaths = entries.map((entry) => path.join(cwd, entry));
|
|
26
|
+
const root = file('../client');
|
|
27
|
+
const app = path.join(root, 'app');
|
|
28
|
+
|
|
29
|
+
log.info(`Creating Vite configuration...`);
|
|
30
|
+
|
|
31
|
+
return defineConfig({
|
|
32
|
+
configFile: false,
|
|
33
|
+
root,
|
|
34
|
+
logLevel: dev ? 'info' : 'silent',
|
|
35
|
+
resolve: {
|
|
36
|
+
alias: [
|
|
37
|
+
{ find: '@fragment/sketches', replacement: fragmentFilepath },
|
|
38
|
+
{ find: '@fragment', replacement: app },
|
|
39
|
+
{
|
|
40
|
+
find: 'three',
|
|
41
|
+
replacement: path.join(cwd, 'node_modules/three'),
|
|
42
|
+
},
|
|
43
|
+
{ find: 'p5', replacement: path.join(cwd, 'node_modules/p5') },
|
|
44
|
+
{
|
|
45
|
+
find: 'ogl',
|
|
46
|
+
replacement: path.join(cwd, 'node_modules/ogl'),
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
plugins: [
|
|
51
|
+
svelte({
|
|
52
|
+
configFile: false,
|
|
53
|
+
onwarn: (warning, handler) => {
|
|
54
|
+
if (dev) {
|
|
55
|
+
handler(warning);
|
|
56
|
+
} else {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
}),
|
|
61
|
+
checkDependencies({
|
|
62
|
+
cwd,
|
|
63
|
+
app,
|
|
64
|
+
entriesPaths,
|
|
65
|
+
build,
|
|
66
|
+
}),
|
|
67
|
+
],
|
|
68
|
+
|
|
69
|
+
define: {
|
|
70
|
+
__CWD__: `${JSON.stringify(cwd)}`,
|
|
71
|
+
__FRAGMENT_PORT__: undefined,
|
|
72
|
+
__START_TIME__: Date.now(),
|
|
73
|
+
__SEED__: Date.now(),
|
|
74
|
+
__BUILD__: build,
|
|
75
|
+
__DEV__: !build,
|
|
76
|
+
},
|
|
77
|
+
optimizeDeps: {
|
|
78
|
+
include: ['convert-length', 'webm-writer', 'changedpi'],
|
|
79
|
+
exclude: ['@fragment/sketches', ...entriesPaths],
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|