@antigenic-oss/paint 0.2.3 → 0.2.5

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
@@ -48,6 +48,27 @@ bun add -g @antigenic-oss/paint
48
48
 
49
49
  Requires Node.js `>=20.9.0`.
50
50
 
51
+ If `paint` is not found after install, add your global bin directory to `PATH`
52
+ once:
53
+
54
+ ```bash
55
+ # Bun
56
+ echo 'export PATH="$HOME/.bun/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc
57
+
58
+ # npm (uses npm global prefix)
59
+ echo 'export PATH="$(npm config get prefix)/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc
60
+
61
+ # pnpm
62
+ pnpm setup
63
+ ```
64
+
65
+ After install, run:
66
+
67
+ ```bash
68
+ paint help
69
+ paint start
70
+ ```
71
+
51
72
  Then use:
52
73
 
53
74
  ```bash
package/bin/paint.js CHANGED
@@ -10,6 +10,21 @@ const { spawn, spawnSync } = require('node:child_process')
10
10
  const ENTRY_REAL_PATH = fs.realpathSync(__filename)
11
11
  const APP_ROOT = path.resolve(path.dirname(ENTRY_REAL_PATH), '..')
12
12
  const STATE_DIR = path.join(os.homedir(), '.paint')
13
+ const APP_PACKAGE_JSON = path.join(APP_ROOT, 'package.json')
14
+ const APP_VERSION = (() => {
15
+ try {
16
+ const pkg = JSON.parse(fs.readFileSync(APP_PACKAGE_JSON, 'utf8'))
17
+ return typeof pkg.version === 'string' ? pkg.version : 'dev'
18
+ } catch {
19
+ return 'dev'
20
+ }
21
+ })()
22
+ const RUNNING_FROM_NODE_MODULES = APP_ROOT.includes(
23
+ `${path.sep}node_modules${path.sep}`,
24
+ )
25
+ const RUNTIME_ROOT = RUNNING_FROM_NODE_MODULES
26
+ ? path.join(STATE_DIR, 'runtime', APP_VERSION)
27
+ : APP_ROOT
13
28
 
14
29
  const APP_STATE_FILE = path.join(STATE_DIR, 'server.json')
15
30
  const TERMINAL_STATE_FILE = path.join(STATE_DIR, 'terminal.json')
@@ -52,6 +67,62 @@ function ensureStateDir() {
52
67
  fs.mkdirSync(STATE_DIR, { recursive: true })
53
68
  }
54
69
 
70
+ function ensureRuntimeRoot() {
71
+ if (!RUNNING_FROM_NODE_MODULES) return APP_ROOT
72
+
73
+ const stampFile = path.join(RUNTIME_ROOT, '.paint-runtime-stamp.json')
74
+ if (fs.existsSync(stampFile)) {
75
+ return RUNTIME_ROOT
76
+ }
77
+
78
+ ensureStateDir()
79
+ fs.mkdirSync(path.dirname(RUNTIME_ROOT), { recursive: true })
80
+
81
+ // Build outside node_modules so Next.js webpack always transpiles TS sources.
82
+ fs.rmSync(RUNTIME_ROOT, { recursive: true, force: true })
83
+ fs.mkdirSync(RUNTIME_ROOT, { recursive: true })
84
+
85
+ const runtimeEntries = [
86
+ 'bin',
87
+ 'public',
88
+ 'src',
89
+ 'next-env.d.ts',
90
+ 'next.config.ts',
91
+ 'postcss.config.mjs',
92
+ 'tsconfig.json',
93
+ 'package.json',
94
+ 'README.md',
95
+ 'LICENSE',
96
+ 'NOTICE',
97
+ ]
98
+
99
+ for (const entry of runtimeEntries) {
100
+ const src = path.join(APP_ROOT, entry)
101
+ const dst = path.join(RUNTIME_ROOT, entry)
102
+ if (!fs.existsSync(src)) continue
103
+
104
+ fs.cpSync(src, dst, { recursive: true, force: true })
105
+ }
106
+
107
+ const runtimeNodeModules = path.join(RUNTIME_ROOT, 'node_modules')
108
+ fs.symlinkSync(path.join(APP_ROOT, 'node_modules'), runtimeNodeModules, 'dir')
109
+
110
+ fs.writeFileSync(
111
+ stampFile,
112
+ JSON.stringify(
113
+ {
114
+ version: APP_VERSION,
115
+ sourceRoot: APP_ROOT,
116
+ preparedAt: now(),
117
+ },
118
+ null,
119
+ 2,
120
+ ),
121
+ )
122
+
123
+ return RUNTIME_ROOT
124
+ }
125
+
55
126
  function now() {
56
127
  return new Date().toISOString()
57
128
  }
@@ -181,12 +252,12 @@ function validatePort(port, flagName) {
181
252
  }
182
253
 
183
254
  function spawnDetached(command, args, opts) {
184
- const { env, logFile } = opts
255
+ const { env, logFile, cwd = APP_ROOT } = opts
185
256
  const fd = fs.openSync(logFile, 'a')
186
257
  fs.writeSync(fd, `\n[${now()}] spawn: ${command} ${args.join(' ')}\n`)
187
258
 
188
259
  const child = spawn(command, args, {
189
- cwd: APP_ROOT,
260
+ cwd,
190
261
  env,
191
262
  detached: true,
192
263
  stdio: ['ignore', fd, fd],
@@ -262,8 +333,9 @@ function ensureNextInstalled() {
262
333
  function ensureBuilt(forceRebuild) {
263
334
  ensureStateDir()
264
335
  ensureNextInstalled()
336
+ const runtimeRoot = ensureRuntimeRoot()
265
337
 
266
- const buildIdPath = path.join(APP_ROOT, '.next', 'BUILD_ID')
338
+ const buildIdPath = path.join(runtimeRoot, '.next', 'BUILD_ID')
267
339
  if (!forceRebuild && fs.existsSync(buildIdPath)) {
268
340
  return
269
341
  }
@@ -274,7 +346,7 @@ function ensureBuilt(forceRebuild) {
274
346
  // Use webpack for packaged/global runtime builds.
275
347
  // Turbopack can fail when the app lives under node_modules.
276
348
  const result = spawnSync(process.execPath, [NEXT_BIN, 'build', '--webpack'], {
277
- cwd: APP_ROOT,
349
+ cwd: runtimeRoot,
278
350
  env: process.env,
279
351
  stdio: 'inherit',
280
352
  })
@@ -302,6 +374,7 @@ async function startApp(options) {
302
374
 
303
375
  ensureBuilt(options.rebuild)
304
376
  ensureStateDir()
377
+ const runtimeRoot = ensureRuntimeRoot()
305
378
 
306
379
  const webChild = spawnDetached(
307
380
  process.execPath,
@@ -314,6 +387,7 @@ async function startApp(options) {
314
387
  options.host,
315
388
  ],
316
389
  {
390
+ cwd: runtimeRoot,
317
391
  env: process.env,
318
392
  logFile: WEB_LOG_FILE,
319
393
  },
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env node
2
+
3
+ const path = require('node:path')
4
+ const fs = require('node:fs')
5
+ const { spawnSync } = require('node:child_process')
6
+
7
+ function detectRcFile() {
8
+ const shell = process.env.SHELL || ''
9
+ const home = process.env.HOME || '~'
10
+ const base = path.basename(shell)
11
+
12
+ if (base === 'zsh') return `${home}/.zshrc`
13
+ if (base === 'bash') return `${home}/.bashrc`
14
+ if (base === 'fish') return `${home}/.config/fish/config.fish`
15
+ return `${home}/.profile`
16
+ }
17
+
18
+ function shouldPrintHint() {
19
+ const isGlobal = process.env.npm_config_global === 'true'
20
+ const ua = process.env.npm_config_user_agent || ''
21
+ const isBun = ua.includes('bun/')
22
+ return isGlobal || isBun
23
+ }
24
+
25
+ function resolveNextBin(appRoot) {
26
+ const directPath = path.join(
27
+ appRoot,
28
+ 'node_modules',
29
+ 'next',
30
+ 'dist',
31
+ 'bin',
32
+ 'next',
33
+ )
34
+ if (fs.existsSync(directPath)) {
35
+ return directPath
36
+ }
37
+
38
+ try {
39
+ return require.resolve('next/dist/bin/next', { paths: [appRoot] })
40
+ } catch {
41
+ return null
42
+ }
43
+ }
44
+
45
+ function tryWarmBuild() {
46
+ if (process.env.PAINT_SKIP_POSTINSTALL_BUILD === '1') {
47
+ return
48
+ }
49
+
50
+ const appRoot = path.resolve(__dirname, '..')
51
+ const buildIdPath = path.join(appRoot, '.next', 'BUILD_ID')
52
+ if (fs.existsSync(buildIdPath)) {
53
+ return
54
+ }
55
+
56
+ const nextBin = resolveNextBin(appRoot)
57
+ if (!nextBin) {
58
+ return
59
+ }
60
+
61
+ console.log('@antigenic-oss/paint: prebuilding runtime (webpack)...')
62
+ const result = spawnSync(
63
+ process.execPath,
64
+ [nextBin, 'build', '--webpack'],
65
+ {
66
+ cwd: appRoot,
67
+ env: process.env,
68
+ stdio: 'inherit',
69
+ },
70
+ )
71
+
72
+ // Keep install resilient across package managers/environments where
73
+ // install-time build is unsupported; first-run build remains as fallback.
74
+ if (result.status !== 0) {
75
+ console.warn(
76
+ '@antigenic-oss/paint: postinstall prebuild failed, will build on first `paint start`.',
77
+ )
78
+ }
79
+ }
80
+
81
+ tryWarmBuild()
82
+
83
+ if (shouldPrintHint()) {
84
+ const rcFile = detectRcFile()
85
+ const exportLine = 'export PATH="$HOME/.bun/bin:$PATH"'
86
+ console.log('\n@antigenic-oss/paint: if `paint` is not found, run:')
87
+ console.log(`echo '${exportLine}' >> ${rcFile} && source ${rcFile}`)
88
+ console.log('Then run: paint help\n')
89
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antigenic-oss/paint",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Visual editor for localhost web projects with a global paint CLI",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/Antigenic-OSS/pAInt",
@@ -46,6 +46,7 @@
46
46
  "dev:terminal": "node ./bin/terminal-server.js",
47
47
  "dev:all": "node ./bin/terminal-server.js & node ./bin/bridge-server.js & next dev --port 4000 --turbopack",
48
48
  "bridge": "node ./bin/bridge-server.js",
49
+ "postinstall": "node ./bin/postinstall.js",
49
50
  "build": "next build",
50
51
  "start": "next start --port 4000",
51
52
  "lint": "biome lint .",
package/tsconfig.json CHANGED
@@ -17,6 +17,7 @@
17
17
  "isolatedModules": true,
18
18
  "jsx": "react-jsx",
19
19
  "incremental": true,
20
+ "baseUrl": ".",
20
21
  "plugins": [
21
22
  {
22
23
  "name": "next"