polen 0.10.0-next.19 → 0.10.0-next.20

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 (71) hide show
  1. package/README.md +2 -2
  2. package/build/api/api.d.ts +1 -0
  3. package/build/api/api.d.ts.map +1 -1
  4. package/build/api/api.js +1 -0
  5. package/build/api/api.js.map +1 -1
  6. package/build/api/builder/builder.d.ts +8 -2
  7. package/build/api/builder/builder.d.ts.map +1 -1
  8. package/build/api/builder/builder.js +5 -18
  9. package/build/api/builder/builder.js.map +1 -1
  10. package/build/api/config/configurator.d.ts +31 -8
  11. package/build/api/config/configurator.d.ts.map +1 -1
  12. package/build/api/config/configurator.js +9 -10
  13. package/build/api/config/configurator.js.map +1 -1
  14. package/build/api/config/merge.d.ts.map +1 -1
  15. package/build/api/config/merge.js +9 -16
  16. package/build/api/config/merge.js.map +1 -1
  17. package/build/api/project/index.d.ts +2 -0
  18. package/build/api/project/index.d.ts.map +1 -0
  19. package/build/api/project/index.js +2 -0
  20. package/build/api/project/index.js.map +1 -0
  21. package/build/api/project/validate.d.ts +11 -0
  22. package/build/api/project/validate.d.ts.map +1 -0
  23. package/build/api/project/validate.js +30 -0
  24. package/build/api/project/validate.js.map +1 -0
  25. package/build/api/vite/plugins/core.d.ts.map +1 -1
  26. package/build/api/vite/plugins/core.js +1 -0
  27. package/build/api/vite/plugins/core.js.map +1 -1
  28. package/build/api/vite/plugins/serve.js +1 -1
  29. package/build/api/vite/plugins/serve.js.map +1 -1
  30. package/build/cli/_/parameters.d.ts +6 -0
  31. package/build/cli/_/parameters.d.ts.map +1 -0
  32. package/build/cli/_/parameters.js +9 -0
  33. package/build/cli/_/parameters.js.map +1 -0
  34. package/build/cli/commands/build.js +17 -10
  35. package/build/cli/commands/build.js.map +1 -1
  36. package/build/cli/commands/config/create.js +12 -1
  37. package/build/cli/commands/config/create.js.map +1 -1
  38. package/build/cli/commands/create.js +3 -5
  39. package/build/cli/commands/create.js.map +1 -1
  40. package/build/cli/commands/dev.js +14 -4
  41. package/build/cli/commands/dev.js.map +1 -1
  42. package/build/lib/graphql-document/components/GraphQLDocument.d.ts.map +1 -1
  43. package/build/lib/graphql-document/components/GraphQLDocument.js +3 -1
  44. package/build/lib/graphql-document/components/GraphQLDocument.js.map +1 -1
  45. package/build/lib/kit-temp.d.ts +33 -0
  46. package/build/lib/kit-temp.d.ts.map +1 -1
  47. package/build/lib/kit-temp.js +48 -0
  48. package/build/lib/kit-temp.js.map +1 -1
  49. package/build/project-data.d.ts +1 -0
  50. package/build/project-data.d.ts.map +1 -1
  51. package/build/template/server/main.js +2 -1
  52. package/build/template/server/main.js.map +1 -1
  53. package/package.json +1 -1
  54. package/src/api/api.ts +1 -0
  55. package/src/api/builder/builder.ts +12 -21
  56. package/src/api/config/configurator.ts +41 -19
  57. package/src/api/config/merge.ts +13 -16
  58. package/src/api/project/index.ts +1 -0
  59. package/src/api/project/validate.ts +41 -0
  60. package/src/api/vite/plugins/core.ts +1 -0
  61. package/src/api/vite/plugins/serve.ts +1 -1
  62. package/src/cli/_/parameters.ts +10 -0
  63. package/src/cli/commands/build.ts +25 -10
  64. package/src/cli/commands/config/create.ts +18 -1
  65. package/src/cli/commands/create.ts +4 -4
  66. package/src/cli/commands/dev.ts +19 -5
  67. package/src/lib/graphql-document/components/GraphQLDocument.tsx +8 -6
  68. package/src/lib/kit-temp.test.ts +85 -1
  69. package/src/lib/kit-temp.ts +51 -0
  70. package/src/project-data.ts +1 -0
  71. package/src/template/server/main.ts +2 -1
@@ -1,11 +1,16 @@
1
1
  // @ts-nocheck
2
2
  import { Api } from '#api/index'
3
- import { Vite } from '#dep/vite/index'
3
+ import { projectParameter } from '#cli/_/parameters'
4
+ import { ensureOptionalAbsoluteWithCwd } from '#lib/kit-temp'
4
5
  import { Command } from '@molt/command'
5
6
  import { z } from 'zod'
6
7
 
7
8
  const args = Command.create()
8
9
  .parameter(`--debug -d`, z.boolean().default(false))
10
+ .parameter(
11
+ `--project -p`,
12
+ projectParameter,
13
+ )
9
14
  .parameter(
10
15
  `--architecture -a`,
11
16
  Api.Config.BuildArchitecture.default('ssg').describe('Which kind of application architecture to output.'),
@@ -14,6 +19,10 @@ const args = Command.create()
14
19
  `--base -b`,
15
20
  z.string().optional().describe('Base public path for deployment (e.g., /my-project/)'),
16
21
  )
22
+ .parameter(
23
+ `--port`,
24
+ z.number().optional().describe('Default port for the SSR application'),
25
+ )
17
26
  .settings({
18
27
  parameters: {
19
28
  environment: {
@@ -27,14 +36,20 @@ const args = Command.create()
27
36
  })
28
37
  .parse()
29
38
 
30
- // HACK:
31
- // todo
32
- // we don't want to lose pretty preting of defaults in Molt but
33
- // we don't want cli defaults to override explicit inputs in the config file either
34
- // we need something like setset and/or an ability in molt to show a default but then have undefined internally etc.
35
- // and now if user passes --no-debug/ --debug false it has no effect which is wrong since its not via default anymore, ... ugh
39
+ const dir = ensureOptionalAbsoluteWithCwd(args.project)
40
+
41
+ if (!await Api.Project.validateProjectDirectory(dir)) {
42
+ process.exit(1)
43
+ }
44
+
36
45
  await Api.Builder.build({
37
- ...(args.debug === false ? {} : { debug: args.debug }),
38
- ...(args.architecture === 'ssg' ? {} : { architecture: args.architecture }),
39
- ...(args.base ? { base: args.base } : {}),
46
+ dir,
47
+ architecture: args.architecture,
48
+ base: args.base,
49
+ server: {
50
+ port: args.port,
51
+ },
52
+ advanced: {
53
+ debug: args.debug,
54
+ },
40
55
  })
@@ -1,6 +1,23 @@
1
+ import { Api } from '#api/index'
2
+ import { projectParameter } from '#cli/_/parameters'
3
+ import { ensureOptionalAbsoluteWithCwd } from '#lib/kit-temp'
4
+ import { Command } from '@molt/command'
1
5
  import { Fs, Path } from '@wollybeard/kit'
2
6
  import consola from 'consola'
3
7
 
8
+ const args = Command.create()
9
+ .parameter(
10
+ `--project -p`,
11
+ projectParameter,
12
+ )
13
+ .parse()
14
+
15
+ const dir = ensureOptionalAbsoluteWithCwd(args.project)
16
+
17
+ if (!await Api.Project.validateProjectDirectory(dir)) {
18
+ process.exit(1)
19
+ }
20
+
4
21
  const fileName = 'polen.config.ts'
5
22
  const fileContent = `import { Polen } from 'polen'
6
23
 
@@ -9,7 +26,7 @@ export default Polen.defineConfig({
9
26
  })
10
27
  `
11
28
 
12
- const filePath = Path.join(process.cwd(), fileName)
29
+ const filePath = Path.join(dir, fileName)
13
30
 
14
31
  const exists = await Fs.exists(filePath)
15
32
  if (exists) {
@@ -1,4 +1,5 @@
1
1
  // @ts-nocheck
2
+ import { Api } from '#api/index'
2
3
  import { Command } from '@molt/command'
3
4
  import { Err, Fs, Manifest, Name, Path, Str } from '@wollybeard/kit'
4
5
  import * as Ansis from 'ansis'
@@ -48,10 +49,9 @@ const args = Command
48
49
 
49
50
  const getProjectRoot = async (): Promise<string> => {
50
51
  if (args.path) {
51
- const stat = await Fs.stat(args.path)
52
- if (Fs.isNotFoundError(stat)) return args.path
53
- if (stat.isDirectory() && await Fs.isEmptyDir(args.path)) return args.path
54
- consola.error(`The given path ${args.path} already exists and is not an empty directory`)
52
+ if (await Api.Project.validateProjectDirectory(args.path, { mustExist: false, mustBeEmpty: true })) {
53
+ return args.path
54
+ }
55
55
  process.exit(1)
56
56
  }
57
57
 
@@ -1,9 +1,10 @@
1
1
  // @ts-nocheck
2
2
  import { Api } from '#api/index'
3
+ import { projectParameter } from '#cli/_/parameters'
3
4
  import { Vite } from '#dep/vite/index'
4
- import { ensureOptionalAbsolute, ensureOptionalAbsoluteWithCwd } from '#lib/kit-temp'
5
+ import { ensureOptionalAbsoluteWithCwd } from '#lib/kit-temp'
5
6
  import { Command } from '@molt/command'
6
- import { Err, Path } from '@wollybeard/kit'
7
+ import { Err } from '@wollybeard/kit'
7
8
  import { z } from 'zod'
8
9
 
9
10
  const args = Command.create()
@@ -11,12 +12,16 @@ const args = Command.create()
11
12
  .parameter(
12
13
  `--project -p`,
13
14
  // @ts-expect-error
14
- z.string().optional().describe(`The path to the project directory. Default is CWD (current working directory).`),
15
+ projectParameter,
15
16
  )
16
17
  .parameter(
17
18
  `--base -b`,
18
19
  z.string().optional().describe('Base public path for deployment (e.g., /my-project/)'),
19
20
  )
21
+ .parameter(
22
+ `--port`,
23
+ z.number().optional().describe('Port to run the development server on'),
24
+ )
20
25
  .settings({
21
26
  parameters: {
22
27
  environment: {
@@ -30,12 +35,21 @@ const args = Command.create()
30
35
  })
31
36
  .parse()
32
37
 
33
- const dir = ensureOptionalAbsoluteWithCwd(args.project) as string
38
+ const dir = ensureOptionalAbsoluteWithCwd(args.project)
39
+
40
+ if (!await Api.Project.validateProjectDirectory(dir)) {
41
+ process.exit(1)
42
+ }
34
43
 
35
44
  const viteUserConfig = await Api.ConfigResolver.fromFile({
36
45
  dir,
37
46
  overrides: {
38
- ...(args.base ? { build: { base: args.base } } : {}),
47
+ build: {
48
+ base: args.base,
49
+ },
50
+ server: {
51
+ port: args.port,
52
+ },
39
53
  advanced: {
40
54
  debug: args.debug,
41
55
  },
@@ -202,13 +202,15 @@ export const GraphQLDocument: React.FC<GraphQLDocumentProps> = ({
202
202
  data-testid='graphql-document'
203
203
  >
204
204
  {/* Base code block */}
205
- {renderCode ? (
206
- renderCode()
207
- ) : (
208
- <pre className='code-block'>
205
+ {renderCode
206
+ ? (
207
+ renderCode()
208
+ )
209
+ : (
210
+ <pre className='code-block'>
209
211
  <code>{children}</code>
210
- </pre>
211
- )}
212
+ </pre>
213
+ )}
212
214
 
213
215
  {/* Interactive overlay layer */}
214
216
  {!plain && isReady && (
@@ -1,6 +1,6 @@
1
1
  import * as fc from 'fast-check'
2
2
  import { describe, expect, test } from 'vitest'
3
- import { objFilter, ObjOmit, ObjPick, objPolicyFilter } from './kit-temp.js'
3
+ import { objFilter, ObjOmit, ObjPick, objPolicyFilter, spreadShallow } from './kit-temp.js'
4
4
 
5
5
  describe('objPolicyFilter', () => {
6
6
  const testObj = { a: 1, b: 2, c: 3, d: 4 }
@@ -137,3 +137,87 @@ describe('property-based tests', () => {
137
137
  )
138
138
  })
139
139
  })
140
+
141
+ describe('mergeShallow', () => {
142
+ test('merges objects while omitting undefined values', () => {
143
+ const base = { a: 1, b: 2, c: 3 }
144
+ const override = { b: undefined, c: 4, d: 5 }
145
+
146
+ expect(spreadShallow(base, override)).toEqual({ a: 1, b: 2, c: 4, d: 5 })
147
+ })
148
+
149
+ test('handles multiple objects', () => {
150
+ const obj1 = { a: 1, b: 2 }
151
+ const obj2 = { b: undefined, c: 3 }
152
+ const obj3 = { c: undefined, d: 4 }
153
+
154
+ expect(spreadShallow(obj1, obj2, obj3)).toEqual({ a: 1, b: 2, c: 3, d: 4 })
155
+ })
156
+
157
+ test('handles empty objects', () => {
158
+ expect(spreadShallow({}, {})).toEqual({})
159
+ expect(spreadShallow({ a: 1 }, {})).toEqual({ a: 1 })
160
+ expect(spreadShallow({}, { a: 1 })).toEqual({ a: 1 })
161
+ })
162
+
163
+ test('handles single object', () => {
164
+ const obj = { a: 1, b: undefined, c: 3 }
165
+ expect(spreadShallow(obj)).toEqual({ a: 1, c: 3 })
166
+ })
167
+
168
+ test('handles no objects', () => {
169
+ expect(spreadShallow()).toEqual({})
170
+ })
171
+
172
+ test('handles undefined objects', () => {
173
+ const obj = { a: 1, b: 2 }
174
+ expect(spreadShallow(undefined, obj, undefined)).toEqual({ a: 1, b: 2 })
175
+ expect(spreadShallow(obj, undefined)).toEqual({ a: 1, b: 2 })
176
+ expect(spreadShallow(undefined, undefined)).toEqual({})
177
+ })
178
+
179
+ test('preserves null values', () => {
180
+ const obj1 = { a: 1, b: null }
181
+ const obj2 = { b: 2, c: null }
182
+ expect(spreadShallow(obj1, obj2)).toEqual({ a: 1, b: 2, c: null })
183
+ })
184
+
185
+ test('preserves false and 0 values', () => {
186
+ const obj1 = { a: true, b: 1 }
187
+ const obj2 = { a: false, b: 0 }
188
+ expect(spreadShallow(obj1, obj2)).toEqual({ a: false, b: 0 })
189
+ })
190
+
191
+ test('property-based: never includes undefined values', () => {
192
+ fc.assert(
193
+ fc.property(
194
+ fc.array(fc.object({ withUndefined: true })),
195
+ (objects) => {
196
+ const result = spreadShallow(...objects)
197
+ Object.values(result).forEach(value => {
198
+ expect(value).not.toBe(undefined)
199
+ })
200
+ },
201
+ ),
202
+ )
203
+ })
204
+
205
+ test('property-based: later objects override earlier ones', () => {
206
+ fc.assert(
207
+ fc.property(
208
+ fc.object(),
209
+ fc.object(),
210
+ fc.string(),
211
+ fc.anything({ withUndefined: false }),
212
+ (obj1, obj2, key, value) => {
213
+ // Set the same key in both objects
214
+ obj1[key] = 'first'
215
+ obj2[key] = value
216
+
217
+ const result = spreadShallow(obj1, obj2)
218
+ expect(result[key]).toBe(value)
219
+ },
220
+ ),
221
+ )
222
+ })
223
+ })
@@ -592,3 +592,54 @@ export const brand = <$BaseType, $BrandName extends string>(
592
592
  ): Brand<$BaseType, $BrandName> => {
593
593
  return value as Brand<$BaseType, $BrandName>
594
594
  }
595
+
596
+ /**
597
+ * Shallow merge objects while omitting undefined values.
598
+ *
599
+ * This utility simplifies the common pattern of conditionally spreading objects
600
+ * to avoid including undefined values that would override existing values.
601
+ *
602
+ * @param objects - Objects to merge (later objects override earlier ones). Undefined objects are ignored.
603
+ * @returns Merged object with undefined values omitted
604
+ *
605
+ * @example
606
+ * ```ts
607
+ * // Instead of:
608
+ * const overrides = {
609
+ * ...(value1 ? { key1: value1 } : {}),
610
+ * ...(value2 ? { key2: value2 } : {}),
611
+ * }
612
+ *
613
+ * // Use:
614
+ * const overrides = mergeShallow({
615
+ * key1: value1,
616
+ * key2: value2,
617
+ * })
618
+ *
619
+ * // Example with config merging:
620
+ * const config = mergeShallow(
621
+ * defaultConfig,
622
+ * userConfig,
623
+ * { debug: args.debug, base: args.base }
624
+ * )
625
+ * // undefined values in the last object won't override earlier values
626
+ * ```
627
+ */
628
+ export const spreadShallow = <T extends object>(...objects: (T | undefined)[]): T => {
629
+ const result = {} as T
630
+
631
+ for (const obj of objects) {
632
+ if (obj === undefined) continue
633
+
634
+ for (const key in obj) {
635
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
636
+ const value = obj[key]
637
+ if (value !== undefined) {
638
+ result[key] = value
639
+ }
640
+ }
641
+ }
642
+ }
643
+
644
+ return result
645
+ }
@@ -8,6 +8,7 @@ export interface ProjectData {
8
8
  paths: Config.Config[`paths`][`project`]
9
9
  navbar: NavbarItem[]
10
10
  server: {
11
+ port: number
11
12
  static: {
12
13
  directory: string
13
14
  route: string
@@ -1,5 +1,6 @@
1
1
  import { serve } from '@hono/node-server' // TODO: support non-node platforms.
2
2
  import { neverCase } from '@wollybeard/kit/language'
3
+ import PROJECT_DATA from 'virtual:polen/project/data.jsonsuper'
3
4
  import { createApp } from './app.js'
4
5
  import { generate } from './ssg/generate.js'
5
6
  import { view } from './view.js'
@@ -10,7 +11,7 @@ if (__BUILDING__) {
10
11
  await generate(view)
11
12
  break
12
13
  case `ssr`:
13
- const port = process.env[`PORT`] ? parseInt(process.env[`PORT`]) : 3001 // todo viteConfigResolved.server.port + 1
14
+ const port = process.env[`PORT`] ? parseInt(process.env[`PORT`]) : PROJECT_DATA.server.port
14
15
  const app = createApp()
15
16
  serve({ fetch: app.fetch, port })
16
17
  break