@crapthings/run3d 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 crapthings
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,16 @@
1
+ # run3d
2
+
3
+ A simple 3D sandbox so you don't have to set up the same project every time.
4
+ JavaScript, Vite, React Three Fiber, Drei, and Tailwind CSS — with a grid and a box to start with.
5
+
6
+ ## Usage
7
+
8
+ Requires Node.js `^20.19.0 || >=22.12.0` and pnpm.
9
+
10
+ ```bash
11
+ npx @crapthings/run3d my-project
12
+ cd my-project
13
+ pnpm dev
14
+ ```
15
+
16
+ Edit `src/Scene.jsx` and start building. Use `--skip-install` to skip dependency installation.
package/bin/run3d.js ADDED
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { access, cp, mkdir, readFile, rename, readdir, writeFile } from 'node:fs/promises'
4
+ import { spawn } from 'node:child_process'
5
+ import { fileURLToPath } from 'node:url'
6
+ import { dirname, join, resolve } from 'node:path'
7
+
8
+ const packageDirectory = dirname(fileURLToPath(import.meta.url))
9
+ const templateDirectory = resolve(packageDirectory, '../template')
10
+ const excludedTemplateEntries = new Set(['dist', 'node_modules'])
11
+ const packageJson = JSON.parse(
12
+ await readFile(resolve(packageDirectory, '../package.json'), 'utf8')
13
+ )
14
+
15
+ function printUsage () {
16
+ console.log(`\nUsage: npx ${packageJson.name} <project-name> [--skip-install]\n\nOptions:\n --skip-install Create files without running pnpm install\n -h, --help Show this help message\n -v, --version Show the CLI version\n`)
17
+ }
18
+
19
+ function fail (message) {
20
+ console.error(`\nerror: ${message}\n`)
21
+ process.exitCode = 1
22
+ }
23
+
24
+ function run (command, args, cwd) {
25
+ return new Promise((resolve, reject) => {
26
+ const child = spawn(command, args, { cwd, stdio: 'inherit', shell: process.platform === 'win32' })
27
+
28
+ child.on('error', reject)
29
+ child.on('close', (code) => {
30
+ if (code === 0) {
31
+ resolve()
32
+ return
33
+ }
34
+ reject(new Error(`${command} ${args.join(' ')} exited with code ${code}`))
35
+ })
36
+ })
37
+ }
38
+
39
+ async function isEmptyDirectory (directory) {
40
+ try {
41
+ return (await readdir(directory)).length === 0
42
+ } catch (error) {
43
+ if (error && error.code === 'ENOENT') return true
44
+ throw error
45
+ }
46
+ }
47
+
48
+ async function exists (path) {
49
+ try {
50
+ await access(path)
51
+ return true
52
+ } catch {
53
+ return false
54
+ }
55
+ }
56
+
57
+ async function main () {
58
+ const argumentsList = process.argv.slice(2)
59
+
60
+ if (argumentsList.includes('-h') || argumentsList.includes('--help')) {
61
+ printUsage()
62
+ return
63
+ }
64
+
65
+ if (argumentsList.includes('-v') || argumentsList.includes('--version')) {
66
+ console.log(packageJson.version)
67
+ return
68
+ }
69
+
70
+ const skipInstall = argumentsList.includes('--skip-install')
71
+ const positionalArguments = argumentsList.filter((argument) => !argument.startsWith('-'))
72
+
73
+ if (positionalArguments.length !== 1) {
74
+ fail('provide exactly one project name.')
75
+ printUsage()
76
+ return
77
+ }
78
+
79
+ const projectName = positionalArguments[0]
80
+ if (!/^[a-z0-9][a-z0-9._-]*$/i.test(projectName)) {
81
+ fail('project name may contain only letters, numbers, dots, underscores, and hyphens.')
82
+ return
83
+ }
84
+
85
+ const targetDirectory = resolve(process.cwd(), projectName)
86
+ if ((await exists(targetDirectory)) && !(await isEmptyDirectory(targetDirectory))) {
87
+ fail(`target directory already exists and is not empty: ${targetDirectory}`)
88
+ return
89
+ }
90
+
91
+ await mkdir(targetDirectory, { recursive: true })
92
+ const templateEntries = (await readdir(templateDirectory)).filter(
93
+ (entry) => !excludedTemplateEntries.has(entry)
94
+ )
95
+ await Promise.all(
96
+ templateEntries.map((entry) =>
97
+ cp(join(templateDirectory, entry), join(targetDirectory, entry), {
98
+ recursive: true,
99
+ force: false,
100
+ errorOnExist: true
101
+ })
102
+ )
103
+ )
104
+
105
+ const renamedTemplateFiles = [
106
+ ['gitignore', '.gitignore'],
107
+ ['npmrc', '.npmrc']
108
+ ]
109
+ for (const [sourceName, targetName] of renamedTemplateFiles) {
110
+ const source = join(targetDirectory, sourceName)
111
+ if (await exists(source)) {
112
+ await rename(source, join(targetDirectory, targetName))
113
+ }
114
+ }
115
+
116
+ const generatedPackagePath = join(targetDirectory, 'package.json')
117
+ const generatedPackage = JSON.parse(await readFile(generatedPackagePath, 'utf8'))
118
+ generatedPackage.name = projectName.toLowerCase()
119
+ await writeFile(generatedPackagePath, `${JSON.stringify(generatedPackage, null, 2)}\n`)
120
+
121
+ console.log(`\nCreated ${projectName} in ${targetDirectory}.`)
122
+
123
+ if (skipInstall) {
124
+ console.log(`\nNext steps:\n cd ${projectName}\n pnpm install\n pnpm dev\n`)
125
+ return
126
+ }
127
+
128
+ try {
129
+ await run('pnpm', ['install'], targetDirectory)
130
+ console.log(`\nReady. Start the app with:\n cd ${projectName}\n pnpm dev\n`)
131
+ } catch (error) {
132
+ if (error && error.code === 'ENOENT') {
133
+ console.log(`\npnpm was not found. Install it, then run:\n cd ${projectName}\n pnpm install\n pnpm dev\n`)
134
+ return
135
+ }
136
+
137
+ fail(`dependencies could not be installed. The project was created; run pnpm install in it to retry.\n${error.message}`)
138
+ }
139
+ }
140
+
141
+ main().catch((error) => fail(error.message))
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@crapthings/run3d",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "0.1.0",
7
+ "description": "Create a minimal Vite, React Three Fiber, and Drei Grid starter project",
8
+ "type": "module",
9
+ "bin": {
10
+ "run3d": "bin/run3d.js"
11
+ },
12
+ "files": [
13
+ "bin",
14
+ "template/gitignore",
15
+ "template/index.html",
16
+ "template/jsconfig.json",
17
+ "template/npmrc",
18
+ "template/package.json",
19
+ "template/pnpm-lock.yaml",
20
+ "template/src",
21
+ "template/vite.config.js"
22
+ ],
23
+ "engines": {
24
+ "node": "^20.19.0 || >=22.12.0"
25
+ },
26
+ "scripts": {
27
+ "lint": "standard \"bin/**/*.js\" \"test/**/*.js\" \"template/src/**/*.{js,jsx}\" template/vite.config.js",
28
+ "lint:fix": "pnpm lint --fix",
29
+ "test": "node --test",
30
+ "pack:check": "npm pack --dry-run"
31
+ },
32
+ "keywords": [
33
+ "vite",
34
+ "react",
35
+ "three",
36
+ "react-three-fiber",
37
+ "drei",
38
+ "starter",
39
+ "cli"
40
+ ],
41
+ "license": "MIT",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "git+https://github.com/crapthings/run3d.git"
45
+ },
46
+ "homepage": "https://github.com/crapthings/run3d#readme",
47
+ "bugs": {
48
+ "url": "https://github.com/crapthings/run3d/issues"
49
+ },
50
+ "devDependencies": {
51
+ "standard": "^17.1.2"
52
+ }
53
+ }
@@ -0,0 +1,4 @@
1
+ node_modules
2
+ dist
3
+ .DS_Store
4
+ *.local
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <meta name="theme-color" content="#e8edf3" />
7
+ <title>Run3D</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.jsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,8 @@
1
+ {
2
+ "compilerOptions": {
3
+ "paths": {
4
+ "@/*": ["./src/*"]
5
+ }
6
+ },
7
+ "include": ["src"]
8
+ }
package/template/npmrc ADDED
@@ -0,0 +1,2 @@
1
+ auto-install-peers=true
2
+ strict-peer-dependencies=true
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "packageManager": "pnpm@11.20.0",
7
+ "engines": {
8
+ "node": "^20.19.0 || >=22.12.0"
9
+ },
10
+ "scripts": {
11
+ "dev": "vite",
12
+ "build": "vite build",
13
+ "preview": "vite preview"
14
+ },
15
+ "dependencies": {
16
+ "@react-three/drei": "^10.7.8",
17
+ "@react-three/fiber": "^9.7.0",
18
+ "react": "19.3.0",
19
+ "react-dom": "19.3.0",
20
+ "three": "^0.186.0"
21
+ },
22
+ "devDependencies": {
23
+ "@tailwindcss/vite": "^4.3.3",
24
+ "@vitejs/plugin-react": "^6.1.1",
25
+ "tailwindcss": "^4.3.3",
26
+ "vite": "^8.3.0"
27
+ }
28
+ }