@platformatic/basic 2.0.0-alpha.3 → 2.0.0-alpha.4

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.
Files changed (69) hide show
  1. package/config.d.ts +159 -0
  2. package/eslint.config.js +3 -1
  3. package/index.js +15 -2
  4. package/lib/base.js +7 -5
  5. package/lib/errors.js +8 -0
  6. package/lib/next.js +105 -0
  7. package/lib/schema.js +23 -0
  8. package/lib/server.js +8 -9
  9. package/lib/utils.js +5 -34
  10. package/lib/vite.js +105 -0
  11. package/lib/worker/child-manager.js +78 -0
  12. package/lib/worker/child-process.js +84 -0
  13. package/lib/worker/next-loader.js +137 -0
  14. package/lib/worker/server-listener.js +29 -0
  15. package/package.json +22 -8
  16. package/schema.json +522 -0
  17. package/test/express.test.js +9 -9
  18. package/test/fastify.test.js +9 -9
  19. package/test/fixtures/next/composer-autodetect-prefix/next.config.mjs +5 -0
  20. package/test/fixtures/next/composer-autodetect-prefix/package.json +15 -0
  21. package/test/fixtures/next/composer-autodetect-prefix/platformatic.application.json +8 -0
  22. package/test/fixtures/next/composer-autodetect-prefix/platformatic.runtime.json +21 -0
  23. package/test/fixtures/next/composer-autodetect-prefix/src/app/layout.js +7 -0
  24. package/test/fixtures/next/composer-autodetect-prefix/src/app/page.js +5 -0
  25. package/test/fixtures/next/composer-with-prefix/next.config.js +1 -0
  26. package/test/fixtures/next/composer-with-prefix/package.json +15 -0
  27. package/test/fixtures/next/composer-with-prefix/platformatic.application.json +11 -0
  28. package/test/fixtures/next/composer-with-prefix/platformatic.runtime.json +21 -0
  29. package/test/fixtures/next/composer-with-prefix/src/app/layout.js +7 -0
  30. package/test/fixtures/next/composer-with-prefix/src/app/page.js +5 -0
  31. package/test/fixtures/next/composer-without-prefix/next.config.mjs +3 -0
  32. package/test/fixtures/next/composer-without-prefix/package.json +15 -0
  33. package/test/fixtures/next/composer-without-prefix/platformatic.application.json +8 -0
  34. package/test/fixtures/next/composer-without-prefix/platformatic.runtime.json +21 -0
  35. package/test/fixtures/next/composer-without-prefix/src/app/layout.js +7 -0
  36. package/test/fixtures/next/composer-without-prefix/src/app/page.js +5 -0
  37. package/test/fixtures/next/standalone/next.config.mjs +3 -0
  38. package/test/fixtures/next/standalone/package.json +15 -0
  39. package/test/fixtures/next/standalone/platformatic.runtime.json +18 -0
  40. package/test/fixtures/next/standalone/src/app/layout.js +7 -0
  41. package/test/fixtures/next/standalone/src/app/page.js +5 -0
  42. package/test/fixtures/platformatic-composer/platformatic.composer.json +20 -0
  43. package/test/fixtures/platformatic-composer/platformatic.no-prefix.composer.json +23 -0
  44. package/test/fixtures/platformatic-composer/plugin.js +9 -0
  45. package/test/fixtures/vite/composer-autodetect-prefix/custom.vite.config.js +3 -0
  46. package/test/fixtures/vite/composer-autodetect-prefix/index.html +13 -0
  47. package/test/fixtures/vite/composer-autodetect-prefix/main.js +3 -0
  48. package/test/fixtures/vite/composer-autodetect-prefix/package.json +14 -0
  49. package/test/fixtures/vite/composer-autodetect-prefix/platformatic.application.json +11 -0
  50. package/test/fixtures/vite/composer-autodetect-prefix/platformatic.runtime.json +21 -0
  51. package/test/fixtures/vite/composer-with-prefix/index.html +13 -0
  52. package/test/fixtures/vite/composer-with-prefix/main.js +3 -0
  53. package/test/fixtures/vite/composer-with-prefix/package.json +14 -0
  54. package/test/fixtures/vite/composer-with-prefix/platformatic.application.json +11 -0
  55. package/test/fixtures/vite/composer-with-prefix/platformatic.runtime.json +21 -0
  56. package/test/fixtures/vite/composer-without-prefix/index.html +13 -0
  57. package/test/fixtures/vite/composer-without-prefix/main.js +3 -0
  58. package/test/fixtures/vite/composer-without-prefix/package.json +14 -0
  59. package/test/fixtures/vite/composer-without-prefix/platformatic.application.json +8 -0
  60. package/test/fixtures/vite/composer-without-prefix/platformatic.runtime.json +21 -0
  61. package/test/fixtures/vite/standalone/custom.vite.config.js +3 -0
  62. package/test/fixtures/vite/standalone/index.html +13 -0
  63. package/test/fixtures/vite/standalone/main.js +3 -0
  64. package/test/fixtures/vite/standalone/package.json +14 -0
  65. package/test/fixtures/vite/standalone/platformatic.runtime.json +18 -0
  66. package/test/helper.js +85 -14
  67. package/test/next.test.js +85 -0
  68. package/test/node.test.js +13 -13
  69. package/test/vite.test.js +89 -0
@@ -0,0 +1,84 @@
1
+ import { ITC } from '@platformatic/itc'
2
+ import { createPinoWritable, DestinationWritable, withResolvers } from '@platformatic/utils'
3
+ import { tracingChannel } from 'node:diagnostics_channel'
4
+ import pino from 'pino'
5
+
6
+ class ChildProcessWritable extends DestinationWritable {
7
+ #itc
8
+
9
+ constructor (options) {
10
+ const { itc, ...opts } = options
11
+
12
+ super(opts)
13
+ this.#itc = itc
14
+ }
15
+
16
+ _send (message) {
17
+ this.#itc.send('log', JSON.stringify(message))
18
+ }
19
+ }
20
+
21
+ class ChildProcess extends ITC {
22
+ #listener
23
+ #child
24
+ #logger
25
+
26
+ constructor () {
27
+ super({})
28
+
29
+ this.listen()
30
+ this.#setupLogger()
31
+ this.#setupServer()
32
+ }
33
+
34
+ _setupListener (listener) {
35
+ this.#listener = listener
36
+ process.on('message', this.#listener)
37
+ }
38
+
39
+ _send (request) {
40
+ process.send(request)
41
+ }
42
+
43
+ _createClosePromise () {
44
+ const { promise } = withResolvers()
45
+ return promise
46
+ }
47
+
48
+ _close () {
49
+ process.kill(process.pid, 'SIGKILL')
50
+ this.#child.removeListener('message', this.#listener)
51
+ }
52
+
53
+ #setupLogger () {
54
+ const destination = new ChildProcessWritable({ itc: this })
55
+ this.#logger = pino({ level: 'info', ...globalThis.platformatic.logger }, destination)
56
+
57
+ Reflect.defineProperty(process, 'stdout', { value: createPinoWritable(this.#logger, 'info') })
58
+ Reflect.defineProperty(process, 'stderr', { value: createPinoWritable(this.#logger, 'error') })
59
+ }
60
+
61
+ #setupServer () {
62
+ const subscribers = {
63
+ asyncStart ({ options }) {
64
+ options.port = 0
65
+ },
66
+ asyncEnd: ({ server }) => {
67
+ tracingChannel('net.server.listen').unsubscribe(subscribers)
68
+
69
+ const { family, address, port } = server.address()
70
+ const url = new URL(family === 'IPv6' ? `http://[${address}]:${port}` : `http://${address}:${port}`).origin
71
+
72
+ this.notify('url', url)
73
+ },
74
+ error: error => {
75
+ tracingChannel('net.server.listen').unsubscribe(subscribers)
76
+ this.notify('error', error)
77
+ },
78
+ }
79
+
80
+ tracingChannel('net.server.listen').subscribe(subscribers)
81
+ }
82
+ }
83
+
84
+ globalThis[Symbol.for('plt.children.itc')] = new ChildProcess()
@@ -0,0 +1,137 @@
1
+ import generate from '@babel/generator'
2
+ import { parse } from '@babel/parser'
3
+ import traverse from '@babel/traverse'
4
+ import {
5
+ blockStatement,
6
+ functionDeclaration,
7
+ identifier,
8
+ restElement,
9
+ returnStatement,
10
+ variableDeclaration,
11
+ variableDeclarator,
12
+ } from '@babel/types'
13
+ import { readFile } from 'node:fs/promises'
14
+
15
+ const originalId = '__pltOriginalNextConfig'
16
+
17
+ let candidates
18
+ let basePath
19
+
20
+ function parseSingleExpression (expr) {
21
+ return parse(expr, { allowAwaitOutsideFunction: true }).program.body[0]
22
+ }
23
+
24
+ /*
25
+ Generates:
26
+ async function (...args) {
27
+ let __pltOriginalNextConfig = $ORIGINAL;
28
+
29
+ if (typeof __pltOriginalNextConfig === 'function') {
30
+ __pltOriginalNextConfig = await __pltOriginalNextConfig(...args);
31
+ }
32
+
33
+ if(typeof __pltOriginalNextConfig === 'undefined') {
34
+ __pltOriginalNextConfig.basePath = basePath
35
+ }
36
+
37
+ return __pltOriginalNextConfig,
38
+ basePath: $BASEPATH
39
+ };
40
+ }
41
+ */
42
+ function createEvaluatorWrapperFunction (original) {
43
+ return functionDeclaration(
44
+ null,
45
+ [restElement(identifier('args'))],
46
+ blockStatement([
47
+ variableDeclaration('let', [variableDeclarator(identifier(originalId), original)]),
48
+ parseSingleExpression(
49
+ `if (typeof ${originalId} === 'function') { ${originalId} = await ${originalId}(...args) }`
50
+ ),
51
+ parseSingleExpression(
52
+ `if (typeof ${originalId}.basePath === 'undefined') { ${originalId}.basePath = "${basePath}" }`
53
+ ),
54
+ parseSingleExpression(`globalThis[Symbol.for('plt.children.itc')].notify('config', ${originalId})`),
55
+ returnStatement(identifier(originalId)),
56
+ ]),
57
+ false,
58
+ true
59
+ )
60
+ }
61
+
62
+ function transformCJS (source) {
63
+ const ast = parse(source.toString(), { sourceType: 'module' })
64
+
65
+ // Manipulate the AST
66
+ traverse.default(ast, {
67
+ AssignmentExpression (path) {
68
+ const { left, right } = path.node
69
+
70
+ // module.exports = $EXPRESSION
71
+ if (left.object.name === 'module' && left.property.name === 'exports') {
72
+ path.node.right = createEvaluatorWrapperFunction(right)
73
+ path.skip()
74
+ }
75
+ },
76
+ })
77
+
78
+ return generate.default(ast).code
79
+ }
80
+
81
+ function transformESM (source) {
82
+ const ast = parse(source.toString(), { sourceType: 'module' })
83
+
84
+ // Manipulate the AST
85
+ traverse.default(ast, {
86
+ ExportDefaultDeclaration (path) {
87
+ const declaration = path.node.declaration
88
+
89
+ // export default [async] function
90
+ if (path.node.declaration.type === 'FunctionDeclaration') {
91
+ path.insertBefore(
92
+ functionDeclaration(
93
+ identifier(originalId + 'Function'),
94
+ declaration.params,
95
+ declaration.body,
96
+ declaration.generator,
97
+ declaration.async
98
+ )
99
+ )
100
+
101
+ path.node.declaration = createEvaluatorWrapperFunction(identifier(originalId + 'Function'))
102
+ } else {
103
+ // export default $EXPRESSION
104
+ path.node.declaration = createEvaluatorWrapperFunction(declaration)
105
+ }
106
+ },
107
+ })
108
+
109
+ return generate.default(ast).code
110
+ }
111
+
112
+ export function initialize (data) {
113
+ // Keep in sync with https://github.com/vercel/next.js/blob/main/packages/next/src/shared/lib/constants.ts
114
+ candidates = ['./next.config.js', './next.config.mjs'].map(c => new URL(c, data.root + '/').toString())
115
+ basePath = data.basePath ?? ''
116
+ }
117
+
118
+ export async function load (url, context, nextLoad) {
119
+ // Load the original file
120
+ const result = await nextLoad(url, context)
121
+
122
+ if (!url.startsWith('file:')) {
123
+ return result
124
+ }
125
+
126
+ if (!candidates.includes(url)) {
127
+ return result
128
+ }
129
+
130
+ if (result.format === 'commonjs') {
131
+ result.source = transformCJS(result.source ?? (await readFile(new URL(result.responseURL ?? url), 'utf-8')))
132
+ } else {
133
+ result.source = transformESM(result.source)
134
+ }
135
+
136
+ return result
137
+ }
@@ -0,0 +1,29 @@
1
+ import { withResolvers } from '@platformatic/utils'
2
+ import { tracingChannel } from 'node:diagnostics_channel'
3
+
4
+ export function createServerListener () {
5
+ const { promise, resolve, reject } = withResolvers()
6
+
7
+ const subscribers = {
8
+ asyncStart ({ options }) {
9
+ options.port = 0
10
+ },
11
+ asyncEnd ({ server }) {
12
+ resolve(server)
13
+ },
14
+ error (error) {
15
+ cancel()
16
+ reject(error)
17
+ },
18
+ }
19
+
20
+ function cancel () {
21
+ tracingChannel('net.server.listen').unsubscribe(subscribers)
22
+ }
23
+
24
+ tracingChannel('net.server.listen').subscribe(subscribers)
25
+ promise.finally(cancel)
26
+ promise.cancel = resolve.bind(null, null)
27
+
28
+ return promise
29
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/basic",
3
- "version": "2.0.0-alpha.3",
3
+ "version": "2.0.0-alpha.4",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -15,27 +15,41 @@
15
15
  },
16
16
  "homepage": "https://github.com/platformatic/platformatic#readme",
17
17
  "dependencies": {
18
+ "@babel/generator": "^7.25.0",
19
+ "@babel/parser": "^7.25.3",
20
+ "@babel/traverse": "^7.25.3",
21
+ "@babel/types": "^7.25.2",
22
+ "@fastify/error": "^4.0.0",
18
23
  "light-my-request": "^5.13.0",
19
24
  "pino": "^9.3.2",
25
+ "semver": "^7.6.3",
20
26
  "undici": "^6.19.5",
21
- "@platformatic/config": "2.0.0-alpha.3"
27
+ "@platformatic/config": "2.0.0-alpha.4",
28
+ "@platformatic/itc": "2.0.0-alpha.4"
22
29
  },
23
30
  "devDependencies": {
24
31
  "borp": "^0.17.0",
25
- "express": "^4.19.2",
26
32
  "eslint": "9",
33
+ "express": "^4.19.2",
27
34
  "fastify": "^4.28.1",
35
+ "json-schema-to-typescript": "^15.0.0",
28
36
  "neostandard": "^0.11.1",
37
+ "next": "^14.2.5",
38
+ "react": "^18.3.1",
39
+ "react-dom": "^18.3.1",
29
40
  "typescript": "^5.5.4",
30
- "@platformatic/composer": "2.0.0-alpha.3",
31
- "@platformatic/service": "2.0.0-alpha.3",
32
- "@platformatic/utils": "2.0.0-alpha.3"
41
+ "vite": "^5.4.0",
42
+ "ws": "^8.18.0",
43
+ "@platformatic/composer": "2.0.0-alpha.4",
44
+ "@platformatic/utils": "2.0.0-alpha.4",
45
+ "@platformatic/service": "2.0.0-alpha.4"
33
46
  },
34
47
  "scripts": {
35
48
  "test": "npm run lint && borp --concurrency=1 --timeout=180000",
36
49
  "coverage": "npm run lint && borp -C -X test -X test/fixtures --concurrency=1 --timeout=180000",
37
- "lint": "eslint",
38
50
  "gen-schema": "node lib/schema.js > schema.json",
39
- "gen-types": "json2ts > config.d.ts < schema.json"
51
+ "gen-types": "json2ts > config.d.ts < schema.json",
52
+ "build": "pnpm run gen-schema && pnpm run gen-types",
53
+ "lint": "eslint"
40
54
  }
41
55
  }