elm-pages 3.0.0-beta.5 → 3.0.0-beta.6

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 (24) hide show
  1. package/codegen/elm-pages-codegen.js +1 -1
  2. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
  3. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  4. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  5. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  6. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1 -1
  7. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +1 -1
  8. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  9. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
  10. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmi +0 -0
  11. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmi +0 -0
  12. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  13. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  14. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  15. package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1 -1
  16. package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +1 -1
  17. package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  18. package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
  19. package/generator/src/build.js +18 -15
  20. package/generator/src/dev-server.js +17 -7
  21. package/generator/src/vite-utils.js +78 -0
  22. package/package.json +2 -2
  23. package/src/Pages/Generate.elm +445 -94
  24. package/src/Pages/Internal/Platform.elm +31 -24
@@ -38904,7 +38904,7 @@ _Platform_export({'Generate':{'init':$author$project$Generate$main(
38904
38904
  $elm$json$Json$Decode$field,
38905
38905
  'templates',
38906
38906
  $elm$json$Json$Decode$list(
38907
- $elm$json$Json$Decode$list($elm$json$Json$Decode$string)))))(0)}});var isBackend = false && typeof isLamdera !== 'undefined'
38907
+ $elm$json$Json$Decode$list($elm$json$Json$Decode$string)))))(0)}});var isBackend = false && typeof isLamdera !== 'undefined';
38908
38908
 
38909
38909
  function _Platform_initialize(flagDecoder, args, init, update, subscriptions, stepperBuilder)
38910
38910
  {
@@ -6692,7 +6692,7 @@ _Platform_export({'Reporter':{'init':$author$project$Reporter$main(
6692
6692
  'paths',
6693
6693
  $elm$json$Json$Decode$list($elm$json$Json$Decode$string)));
6694
6694
  },
6695
- A2($elm$json$Json$Decode$field, 'verbosity', $elm$json$Json$Decode$int)))(0)}});var isBackend = false && typeof isLamdera !== 'undefined'
6695
+ A2($elm$json$Json$Decode$field, 'verbosity', $elm$json$Json$Decode$int)))(0)}});var isBackend = false && typeof isLamdera !== 'undefined';
6696
6696
 
6697
6697
  function _Platform_initialize(flagDecoder, args, init, update, subscriptions, stepperBuilder)
6698
6698
  {
@@ -25732,7 +25732,7 @@ _Platform_export({'Runner':{'init':$author$project$Runner$main(
25732
25732
  },
25733
25733
  A2($elm$json$Json$Decode$field, 'fuzzRuns', $elm$json$Json$Decode$int));
25734
25734
  },
25735
- A2($elm$json$Json$Decode$field, 'initialSeed', $elm$json$Json$Decode$int)))(0)}});var isBackend = false && typeof isLamdera !== 'undefined'
25735
+ A2($elm$json$Json$Decode$field, 'initialSeed', $elm$json$Json$Decode$int)))(0)}});var isBackend = false && typeof isLamdera !== 'undefined';
25736
25736
 
25737
25737
  function _Platform_initialize(flagDecoder, args, init, update, subscriptions, stepperBuilder)
25738
25738
  {
@@ -75,7 +75,7 @@ console.elmlog = (str) => logs.push(str + "\n");
75
75
  const { Elm } = require("./Runner.elm.js");
76
76
 
77
77
  // Start the Elm app
78
- const flags = { initialSeed: 4067606959, fuzzRuns: 100, filter: null };
78
+ const flags = { initialSeed: 1035103192, fuzzRuns: 100, filter: null };
79
79
  const app = Elm.Runner.init({ flags: flags });
80
80
 
81
81
  // Record the timing at which we received the last "runTest" message
@@ -75,19 +75,19 @@ let testsCount, todoTests;
75
75
  let reporter;
76
76
  let runners = [];
77
77
  let working = false;
78
- let workersCount = 2;
78
+ let workersCount = 10;
79
79
  let startWorkCallback = function(){};
80
80
  const verbosity = 0;
81
81
 
82
82
  // Create a long lived reporter worker
83
83
  const { Elm } = require("./Reporter.elm.js");
84
84
  const flags = {
85
- initialSeed: 4067606959,
85
+ initialSeed: 1035103192,
86
86
  fuzzRuns: 100,
87
- mode: "consoleNoColor",
87
+ mode: "consoleColor",
88
88
  verbosity: verbosity,
89
89
  globs: [],
90
- paths: ["/home/runner/work/elm-pages-v3-beta/elm-pages-v3-beta/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm"],
90
+ paths: ["/Users/dillonkearns/src/github.com/dillonkearns/elm-pages-v3-beta/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm"],
91
91
  };
92
92
  reporter = Elm.Reporter.init({ flags: flags });
93
93
 
@@ -6692,7 +6692,7 @@ _Platform_export({'Reporter':{'init':$author$project$Reporter$main(
6692
6692
  'paths',
6693
6693
  $elm$json$Json$Decode$list($elm$json$Json$Decode$string)));
6694
6694
  },
6695
- A2($elm$json$Json$Decode$field, 'verbosity', $elm$json$Json$Decode$int)))(0)}});var isBackend = false && typeof isLamdera !== 'undefined'
6695
+ A2($elm$json$Json$Decode$field, 'verbosity', $elm$json$Json$Decode$int)))(0)}});var isBackend = false && typeof isLamdera !== 'undefined';
6696
6696
 
6697
6697
  function _Platform_initialize(flagDecoder, args, init, update, subscriptions, stepperBuilder)
6698
6698
  {
@@ -27514,7 +27514,7 @@ _Platform_export({'Runner':{'init':$author$project$Runner$main(
27514
27514
  },
27515
27515
  A2($elm$json$Json$Decode$field, 'fuzzRuns', $elm$json$Json$Decode$int));
27516
27516
  },
27517
- A2($elm$json$Json$Decode$field, 'initialSeed', $elm$json$Json$Decode$int)))(0)}});var isBackend = false && typeof isLamdera !== 'undefined'
27517
+ A2($elm$json$Json$Decode$field, 'initialSeed', $elm$json$Json$Decode$int)))(0)}});var isBackend = false && typeof isLamdera !== 'undefined';
27518
27518
 
27519
27519
  function _Platform_initialize(flagDecoder, args, init, update, subscriptions, stepperBuilder)
27520
27520
  {
@@ -75,7 +75,7 @@ console.elmlog = (str) => logs.push(str + "\n");
75
75
  const { Elm } = require("./Runner.elm.js");
76
76
 
77
77
  // Start the Elm app
78
- const flags = { initialSeed: 1129837679, fuzzRuns: 100, filter: null };
78
+ const flags = { initialSeed: 1889275192, fuzzRuns: 100, filter: null };
79
79
  const app = Elm.Runner.init({ flags: flags });
80
80
 
81
81
  // Record the timing at which we received the last "runTest" message
@@ -75,19 +75,19 @@ let testsCount, todoTests;
75
75
  let reporter;
76
76
  let runners = [];
77
77
  let working = false;
78
- let workersCount = 2;
78
+ let workersCount = 10;
79
79
  let startWorkCallback = function(){};
80
80
  const verbosity = 0;
81
81
 
82
82
  // Create a long lived reporter worker
83
83
  const { Elm } = require("./Reporter.elm.js");
84
84
  const flags = {
85
- initialSeed: 1129837679,
85
+ initialSeed: 1889275192,
86
86
  fuzzRuns: 100,
87
- mode: "consoleNoColor",
87
+ mode: "consoleColor",
88
88
  verbosity: verbosity,
89
89
  globs: [],
90
- paths: ["/home/runner/work/elm-pages-v3-beta/elm-pages-v3-beta/generator/review/tests/Pages/Review/NoContractViolationsTest.elm"],
90
+ paths: ["/Users/dillonkearns/src/github.com/dillonkearns/elm-pages-v3-beta/generator/review/tests/Pages/Review/NoContractViolationsTest.elm"],
91
91
  };
92
92
  reporter = Elm.Reporter.init({ flags: flags });
93
93
 
@@ -16,6 +16,7 @@ const { build } = require("vite");
16
16
  const preRenderHtml = require("./pre-render-html.js");
17
17
  const esbuild = require("esbuild");
18
18
  const { createHash } = require("crypto");
19
+ const { merge_vite_configs } = require("./vite-utils.js");
19
20
 
20
21
  let pool = [];
21
22
  let pagesReady;
@@ -92,23 +93,25 @@ async function run(options) {
92
93
  );
93
94
  return {};
94
95
  });
95
- const viteConfig = config.vite || {};
96
-
97
- const buildComplete = build({
98
- configFile: false,
99
- root: process.cwd(),
100
- base: options.base,
101
- ssr: false,
102
-
103
- build: {
104
- manifest: true,
105
- outDir: "dist",
106
- rollupOptions: {
107
- input: "elm-stuff/elm-pages/index.html",
96
+ const viteConfig = merge_vite_configs(
97
+ {
98
+ configFile: false,
99
+ root: process.cwd(),
100
+ base: options.base,
101
+ ssr: false,
102
+
103
+ build: {
104
+ manifest: true,
105
+ outDir: "dist",
106
+ rollupOptions: {
107
+ input: "elm-stuff/elm-pages/index.html",
108
+ },
108
109
  },
109
110
  },
110
- ...viteConfig,
111
- });
111
+ config.vite || {}
112
+ );
113
+
114
+ const buildComplete = build(viteConfig);
112
115
  const compileClientDone = compileElm(options);
113
116
  await buildComplete;
114
117
  await compileClientDone;
@@ -24,6 +24,7 @@ const busboy = require("busboy");
24
24
  const { createServer: createViteServer } = require("vite");
25
25
  const cliVersion = require("../../package.json").version;
26
26
  const esbuild = require("esbuild");
27
+ const { merge_vite_configs } = require("./vite-utils.js");
27
28
 
28
29
  /**
29
30
  * @param {{ port: string; base: string; https: boolean; debug: boolean; }} options
@@ -131,13 +132,22 @@ async function start(options) {
131
132
  );
132
133
  return {};
133
134
  });
134
- const vite = await createViteServer({
135
- server: { middlewareMode: "ssr", base: options.base, port: options.port },
136
- configFile: false,
137
- root: process.cwd(),
138
- base: options.base,
139
- ...viteConfig,
140
- });
135
+ const vite = await createViteServer(
136
+ merge_vite_configs(
137
+ {
138
+ server: {
139
+ middlewareMode: "ssr",
140
+ base: options.base,
141
+ port: options.port,
142
+ },
143
+ configFile: false,
144
+ root: process.cwd(),
145
+ base: options.base,
146
+ },
147
+
148
+ viteConfig
149
+ )
150
+ );
141
151
  esbuild
142
152
  .build({
143
153
  entryPoints: ["./port-data-source"],
@@ -0,0 +1,78 @@
1
+ /** This code is from https://github.com/sveltejs/kit/blob/3b457f67d4d7c59fc63bb3f600a490e4dacc2e62/packages/kit/src/exports/vite/utils.js */
2
+
3
+ /**
4
+ * @param {...import('vite').UserConfig} configs
5
+ * @returns {import('vite').UserConfig}
6
+ */
7
+ function merge_vite_configs(...configs) {
8
+ return deep_merge(
9
+ ...configs.map((config) => ({
10
+ ...config,
11
+ resolve: {
12
+ ...config.resolve,
13
+ alias: normalize_alias(config.resolve?.alias || {}),
14
+ },
15
+ }))
16
+ );
17
+ }
18
+
19
+ /**
20
+ * Takes zero or more objects and returns a new object that has all the values
21
+ * deeply merged together. None of the original objects will be mutated at any
22
+ * level, and the returned object will have no references to the original
23
+ * objects at any depth. If there's a conflict the last one wins, except for
24
+ * arrays which will be combined.
25
+ * @param {...Object} objects
26
+ * @returns {Record<string, any>} the merged object
27
+ */
28
+ function deep_merge(...objects) {
29
+ const result = {};
30
+ /** @type {string[]} */
31
+ objects.forEach((o) => merge_into(result, o));
32
+ return result;
33
+ }
34
+
35
+ /**
36
+ * normalize kit.vite.resolve.alias as an array
37
+ * @param {import('vite').AliasOptions} o
38
+ * @returns {import('vite').Alias[]}
39
+ */
40
+ function normalize_alias(o) {
41
+ if (Array.isArray(o)) return o;
42
+ return Object.entries(o).map(([find, replacement]) => ({
43
+ find,
44
+ replacement,
45
+ }));
46
+ }
47
+
48
+ /**
49
+ * Merges b into a, recursively, mutating a.
50
+ * @param {Record<string, any>} a
51
+ * @param {Record<string, any>} b
52
+ */
53
+ function merge_into(a, b) {
54
+ /**
55
+ * Checks for "plain old Javascript object", typically made as an object
56
+ * literal. Excludes Arrays and built-in types like Buffer.
57
+ * @param {any} x
58
+ */
59
+ const is_plain_object = (x) =>
60
+ typeof x === "object" && x.constructor === Object;
61
+
62
+ for (const prop in b) {
63
+ if (is_plain_object(b[prop])) {
64
+ if (!is_plain_object(a[prop])) {
65
+ a[prop] = {};
66
+ }
67
+ merge_into(a[prop], b[prop]);
68
+ } else if (Array.isArray(b[prop])) {
69
+ if (!Array.isArray(a[prop])) {
70
+ a[prop] = [];
71
+ }
72
+ a[prop].push(...b[prop]);
73
+ } else {
74
+ a[prop] = b[prop];
75
+ }
76
+ }
77
+ }
78
+ module.exports = { merge_vite_configs };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elm-pages",
3
- "version": "3.0.0-beta.5",
3
+ "version": "3.0.0-beta.6",
4
4
  "homepage": "https://elm-pages.com",
5
5
  "moduleResolution": "node",
6
6
  "description": "Type-safe static sites, written in pure elm with your own custom elm-markup syntax.",
@@ -45,7 +45,7 @@
45
45
  "object-hash": "^2.2.0",
46
46
  "serve-static": "^1.15.0",
47
47
  "terser": "^5.14.2",
48
- "vite": "^3.0.9",
48
+ "vite": "^3.1.8",
49
49
  "which": "^2.0.2"
50
50
  },
51
51
  "devDependencies": {
@@ -1,8 +1,28 @@
1
- module Pages.Generate exposing (Type(..), serverRender, buildWithLocalState, buildNoState, Builder)
1
+ module Pages.Generate exposing
2
+ ( buildWithLocalState, buildNoState, Builder
3
+ , Type(..)
4
+ , serverRender
5
+ , preRender, single
6
+ )
2
7
 
3
8
  {-|
4
9
 
5
- @docs Type, serverRender, buildWithLocalState, buildNoState, Builder
10
+
11
+ ## Initializing the Generator Builder
12
+
13
+ @docs buildWithLocalState, buildNoState, Builder
14
+
15
+ @docs Type
16
+
17
+
18
+ ## Generating Server-Rendered Pages
19
+
20
+ @docs serverRender
21
+
22
+
23
+ ## Generating pre-rendered pages
24
+
25
+ @docs preRender, single
6
26
 
7
27
  -}
8
28
 
@@ -30,12 +50,18 @@ typeToDeclaration name type_ =
30
50
 
31
51
  {-| -}
32
52
  type Builder
33
- = Builder
53
+ = ServerRender
34
54
  { data : ( Type, Elm.Expression -> Elm.Expression )
35
55
  , action : ( Type, Elm.Expression -> Elm.Expression )
36
56
  , head : Elm.Expression -> Elm.Expression
37
57
  , moduleName : List String
38
58
  }
59
+ | PreRender
60
+ { data : ( Type, Elm.Expression -> Elm.Expression )
61
+ , pages : Maybe Elm.Expression
62
+ , head : Elm.Expression -> Elm.Expression
63
+ , moduleName : List String
64
+ }
39
65
 
40
66
 
41
67
  {-| -}
@@ -47,7 +73,56 @@ serverRender :
47
73
  }
48
74
  -> Builder
49
75
  serverRender =
50
- Builder
76
+ ServerRender
77
+
78
+
79
+ {-| -}
80
+ preRender :
81
+ { data : ( Type, Elm.Expression -> Elm.Expression )
82
+ , pages : Elm.Expression
83
+ , head : Elm.Expression -> Elm.Expression
84
+ , moduleName : List String
85
+ }
86
+ -> Builder
87
+ preRender input =
88
+ --let
89
+ -- hasDynamicRouteSegments : Bool
90
+ -- hasDynamicRouteSegments =
91
+ -- RoutePattern.fromModuleName input.moduleName
92
+ -- -- TODO give error if not parseable here
93
+ -- |> Maybe.map RoutePattern.hasRouteParams
94
+ -- |> Maybe.withDefault False
95
+ --in
96
+ PreRender
97
+ { data = input.data
98
+ , pages =
99
+ input.pages
100
+ |> Elm.withType
101
+ (Elm.Annotation.namedWith [ "DataSource" ]
102
+ "DataSource"
103
+ [ Elm.Annotation.list <| Elm.Annotation.named [] "RouteParams"
104
+ ]
105
+ )
106
+ |> Just
107
+ , head = input.head
108
+ , moduleName = input.moduleName
109
+ }
110
+
111
+
112
+ {-| -}
113
+ single :
114
+ { data : ( Type, Elm.Expression )
115
+ , head : Elm.Expression -> Elm.Expression
116
+ , moduleName : List String
117
+ }
118
+ -> Builder
119
+ single input =
120
+ PreRender
121
+ { data = ( Tuple.first input.data, \_ -> Tuple.second input.data )
122
+ , pages = Nothing
123
+ , head = input.head
124
+ , moduleName = input.moduleName
125
+ }
51
126
 
52
127
 
53
128
  {-| -}
@@ -56,20 +131,42 @@ buildNoState :
56
131
  }
57
132
  -> Builder
58
133
  -> Elm.File
59
- buildNoState definitions (Builder builder) =
60
- userFunction builder.moduleName
61
- { view = \_ -> definitions.view
62
- , localState = Nothing
63
- , data = builder.data |> Tuple.second
64
- , action = builder.action |> Tuple.second
65
- , head = builder.head
66
- , types =
67
- { model = Alias (Elm.Annotation.record [])
68
- , msg = Alias Elm.Annotation.unit
69
- , data = builder.data |> Tuple.first
70
- , actionData = builder.action |> Tuple.first
71
- }
72
- }
134
+ buildNoState definitions builder_ =
135
+ case builder_ of
136
+ ServerRender builder ->
137
+ userFunction builder.moduleName
138
+ { view = \_ -> definitions.view
139
+ , localState = Nothing
140
+ , data = builder.data |> Tuple.second
141
+ , action = builder.action |> Tuple.second |> Action
142
+ , head = builder.head
143
+ , types =
144
+ { model = Alias (Elm.Annotation.record [])
145
+ , msg = Alias Elm.Annotation.unit
146
+ , data = builder.data |> Tuple.first
147
+ , actionData = builder.action |> Tuple.first
148
+ }
149
+ }
150
+
151
+ PreRender builder ->
152
+ userFunction builder.moduleName
153
+ { view = \_ -> definitions.view
154
+ , localState = Nothing
155
+ , data = builder.data |> Tuple.second
156
+ , action = builder.pages |> Pages
157
+ , head = builder.head
158
+ , types =
159
+ { model = Alias (Elm.Annotation.record [])
160
+ , msg = Alias Elm.Annotation.unit
161
+ , data = builder.data |> Tuple.first
162
+ , actionData =
163
+ Elm.Annotation.namedWith [ "DataSource" ]
164
+ "DataSource"
165
+ [ Elm.Annotation.list (Elm.Annotation.named [] "RouteParams")
166
+ ]
167
+ |> Alias
168
+ }
169
+ }
73
170
 
74
171
 
75
172
  {-| -}
@@ -83,25 +180,57 @@ buildWithLocalState :
83
180
  }
84
181
  -> Builder
85
182
  -> Elm.File
86
- buildWithLocalState definitions (Builder builder) =
87
- userFunction builder.moduleName
88
- { view = definitions.view
89
- , localState =
90
- Just
91
- { update = definitions.update
92
- , init = definitions.init
93
- , subscriptions = definitions.subscriptions
183
+ buildWithLocalState definitions builder_ =
184
+ case builder_ of
185
+ ServerRender builder ->
186
+ userFunction builder.moduleName
187
+ { view = definitions.view
188
+ , localState =
189
+ Just
190
+ { update = definitions.update
191
+ , init = definitions.init
192
+ , subscriptions = definitions.subscriptions
193
+ }
194
+ , data = builder.data |> Tuple.second
195
+ , action = builder.action |> Tuple.second |> Action
196
+ , head = builder.head
197
+ , types =
198
+ { model = definitions.model
199
+ , msg = definitions.msg
200
+ , data = builder.data |> Tuple.first
201
+ , actionData = builder.action |> Tuple.first
202
+ }
203
+ }
204
+
205
+ PreRender builder ->
206
+ userFunction builder.moduleName
207
+ { view = definitions.view
208
+ , localState =
209
+ Just
210
+ { update = definitions.update
211
+ , init = definitions.init
212
+ , subscriptions = definitions.subscriptions
213
+ }
214
+ , data = builder.data |> Tuple.second
215
+ , action = builder.pages |> Pages
216
+ , head = builder.head
217
+ , types =
218
+ { model = definitions.model
219
+ , msg = definitions.msg
220
+ , data = builder.data |> Tuple.first
221
+ , actionData =
222
+ Elm.Annotation.namedWith [ "DataSource" ]
223
+ "DataSource"
224
+ [ Elm.Annotation.list (Elm.Annotation.named [] "RouteParams")
225
+ ]
226
+ |> Alias
227
+ }
94
228
  }
95
- , data = builder.data |> Tuple.second
96
- , action = builder.action |> Tuple.second
97
- , head = builder.head
98
- , types =
99
- { model = definitions.model
100
- , msg = definitions.msg
101
- , data = builder.data |> Tuple.first
102
- , actionData = builder.action |> Tuple.first
103
- }
104
- }
229
+
230
+
231
+ type ActionOrPages
232
+ = Action (Elm.Expression -> Elm.Expression)
233
+ | Pages (Maybe Elm.Expression)
105
234
 
106
235
 
107
236
  {-| -}
@@ -116,7 +245,7 @@ userFunction :
116
245
  , subscriptions : Elm.Expression -> Elm.Expression -> Elm.Expression -> Elm.Expression -> Elm.Expression -> Elm.Expression
117
246
  }
118
247
  , data : Elm.Expression -> Elm.Expression
119
- , action : Elm.Expression -> Elm.Expression
248
+ , action : ActionOrPages
120
249
  , head : Elm.Expression -> Elm.Expression
121
250
  , types : { model : Type, msg : Type, data : Type, actionData : Type }
122
251
  }
@@ -225,25 +354,84 @@ userFunction moduleName definitions =
225
354
  }
226
355
  )
227
356
 
228
- dataFn : { declaration : Elm.Declaration, call : Elm.Expression -> Elm.Expression, callFrom : List String -> Elm.Expression -> Elm.Expression }
357
+ dataFn : { declaration : Elm.Declaration, call : List Elm.Expression -> Elm.Expression, callFrom : List String -> List Elm.Expression -> Elm.Expression }
229
358
  dataFn =
230
- Elm.Declare.fn "data"
231
- ( "routeParams"
232
- , "RouteParams"
233
- |> Elm.Annotation.named []
234
- |> Just
235
- )
236
- (definitions.data >> Elm.withType (myType "Data"))
359
+ case definitions.action of
360
+ Pages Nothing ->
361
+ Elm.Declare.function "data"
362
+ []
363
+ (\_ ->
364
+ definitions.data Elm.unit
365
+ |> Elm.withType
366
+ (case definitions.action of
367
+ Pages _ ->
368
+ Elm.Annotation.namedWith [ "DataSource" ]
369
+ "DataSource"
370
+ [ Elm.Annotation.named [] "Data"
371
+ ]
372
+
373
+ Action _ ->
374
+ myType "Data"
375
+ )
376
+ )
237
377
 
238
- actionFn : { declaration : Elm.Declaration, call : Elm.Expression -> Elm.Expression, callFrom : List String -> Elm.Expression -> Elm.Expression }
378
+ _ ->
379
+ Elm.Declare.function "data"
380
+ [ ( "routeParams"
381
+ , "RouteParams"
382
+ |> Elm.Annotation.named []
383
+ |> Just
384
+ )
385
+ ]
386
+ (\args ->
387
+ case args of
388
+ [ arg ] ->
389
+ definitions.data arg
390
+ |> Elm.withType
391
+ (case definitions.action of
392
+ Pages _ ->
393
+ Elm.Annotation.namedWith [ "DataSource" ]
394
+ "DataSource"
395
+ [ Elm.Annotation.named [] "Data"
396
+ ]
397
+
398
+ Action _ ->
399
+ myType "Data"
400
+ )
401
+
402
+ _ ->
403
+ Elm.unit
404
+ )
405
+
406
+ actionFn : Maybe { declaration : Elm.Declaration, call : List Elm.Expression -> Elm.Expression, callFrom : List String -> List Elm.Expression -> Elm.Expression }
239
407
  actionFn =
240
- Elm.Declare.fn "action"
241
- ( "routeParams"
242
- , "RouteParams"
243
- |> Elm.Annotation.named []
244
- |> Just
245
- )
246
- (definitions.action >> Elm.withType (myType "ActionData"))
408
+ case definitions.action of
409
+ Action action_ ->
410
+ Elm.Declare.function "action"
411
+ [ ( "routeParams"
412
+ , "RouteParams"
413
+ |> Elm.Annotation.named []
414
+ |> Just
415
+ )
416
+ ]
417
+ (\args ->
418
+ case args of
419
+ [ arg ] ->
420
+ action_ arg |> Elm.withType (myType "ActionData")
421
+
422
+ _ ->
423
+ Elm.unit
424
+ )
425
+ |> Just
426
+
427
+ Pages pages_ ->
428
+ pages_
429
+ |> Maybe.map
430
+ (\justPagesExpression ->
431
+ Elm.Declare.function "pages"
432
+ []
433
+ (\_ -> justPagesExpression)
434
+ )
247
435
 
248
436
  headFn : { declaration : Elm.Declaration, call : Elm.Expression -> Elm.Expression, callFrom : List String -> Elm.Expression -> Elm.Expression }
249
437
  headFn =
@@ -268,49 +456,87 @@ userFunction moduleName definitions =
268
456
  )
269
457
  )
270
458
  , Elm.declaration "route"
271
- (serverRender_
272
- { action =
273
- \routeParams ->
274
- actionFn.call routeParams
275
- |> Elm.withType (myType "ActionData")
276
- , data =
277
- \routeParams ->
278
- dataFn.call routeParams
279
- |> Elm.withType (myType "Data")
280
- , head = headFn.call
281
- }
282
- |> (case localDefinitions of
283
- Just local ->
284
- buildWithLocalState_
285
- { view = viewFn.call
286
- , update = local.updateFn.call
287
- , init = local.initFn.call
288
- , subscriptions = local.subscriptionsFn.call
289
- }
290
- >> Elm.withType
291
- (Elm.Annotation.namedWith [ "RouteBuilder" ]
292
- "StatefulRoute"
293
- [ localType "RouteParams"
294
- , localType "Data"
295
- , localType "ActionData"
296
- , localType "Model"
297
- , localType "Msg"
298
- ]
299
- )
459
+ (case definitions.action of
460
+ Action _ ->
461
+ serverRender_
462
+ { action =
463
+ \routeParams ->
464
+ actionFn
465
+ |> Maybe.map
466
+ (\justActionFn ->
467
+ justActionFn.call [ routeParams ]
468
+ |> Elm.withType (myType "ActionData")
469
+ )
470
+ |> Maybe.withDefault Elm.unit
471
+ , data =
472
+ \routeParams ->
473
+ dataFn.call [ routeParams ]
474
+ |> Elm.withType (myType "Data")
475
+ , head = headFn.call
476
+ }
300
477
 
478
+ Pages _ ->
479
+ (case actionFn of
301
480
  Nothing ->
302
- buildNoState_
303
- { view = viewFn.call Elm.unit
481
+ single_
482
+ { data =
483
+ dataFn.call []
484
+ |> Elm.withType
485
+ (Elm.Annotation.namedWith [ "DataSource" ]
486
+ "DataSource"
487
+ [ Elm.Annotation.named [] "Data"
488
+ ]
489
+ )
490
+ , head = headFn.call
304
491
  }
305
- >> Elm.withType
306
- (Elm.Annotation.namedWith [ "RouteBuilder" ]
307
- "StatelessRoute"
308
- [ localType "RouteParams"
309
- , localType "Data"
310
- , localType "ActionData"
311
- ]
312
- )
313
- )
492
+
493
+ Just justActionFn ->
494
+ preRender_
495
+ { pages = justActionFn.call []
496
+ , data =
497
+ \routeParams ->
498
+ dataFn.call [ routeParams ]
499
+ |> Elm.withType
500
+ (Elm.Annotation.namedWith [ "DataSource" ]
501
+ "DataSource"
502
+ [ Elm.Annotation.named [] "Data"
503
+ ]
504
+ )
505
+ , head = headFn.call
506
+ }
507
+ )
508
+ |> (case localDefinitions of
509
+ Just local ->
510
+ buildWithLocalState_
511
+ { view = viewFn.call
512
+ , update = local.updateFn.call
513
+ , init = local.initFn.call
514
+ , subscriptions = local.subscriptionsFn.call
515
+ }
516
+ >> Elm.withType
517
+ (Elm.Annotation.namedWith [ "RouteBuilder" ]
518
+ "StatefulRoute"
519
+ [ localType "RouteParams"
520
+ , localType "Data"
521
+ , localType "ActionData"
522
+ , localType "Model"
523
+ , localType "Msg"
524
+ ]
525
+ )
526
+
527
+ Nothing ->
528
+ buildNoState_
529
+ { view = viewFn.call Elm.unit
530
+ }
531
+ >> Elm.withType
532
+ (Elm.Annotation.namedWith [ "RouteBuilder" ]
533
+ "StatelessRoute"
534
+ [ localType "RouteParams"
535
+ , localType "Data"
536
+ , localType "ActionData"
537
+ ]
538
+ )
539
+ )
314
540
  )
315
541
  ]
316
542
  ++ (case localDefinitions of
@@ -326,10 +552,13 @@ userFunction moduleName definitions =
326
552
  ++ [ definitions.types.data |> typeToDeclaration "Data"
327
553
  , definitions.types.actionData |> typeToDeclaration "ActionData"
328
554
  , dataFn.declaration
329
- , actionFn.declaration
330
555
  , headFn.declaration
331
556
  , viewFn.declaration
332
557
  ]
558
+ ++ ([ actionFn |> Maybe.map .declaration
559
+ ]
560
+ |> List.filterMap identity
561
+ )
333
562
  )
334
563
 
335
564
 
@@ -463,6 +692,128 @@ serverRender_ serverRenderArg =
463
692
  ]
464
693
 
465
694
 
695
+ preRender_ :
696
+ { data : Elm.Expression -> Elm.Expression
697
+ , pages : Elm.Expression
698
+ , head : Elm.Expression -> Elm.Expression
699
+ }
700
+ -> Elm.Expression
701
+ preRender_ serverRenderArg =
702
+ Elm.apply
703
+ (Elm.value
704
+ { importFrom = [ "RouteBuilder" ]
705
+ , name = "preRender"
706
+ , annotation =
707
+ Just
708
+ (Elm.Annotation.function
709
+ [ Elm.Annotation.record
710
+ [ ( "data"
711
+ , Elm.Annotation.function
712
+ [ Elm.Annotation.var "routeParams" ]
713
+ (Elm.Annotation.namedWith
714
+ [ "DataSource" ]
715
+ "DataSource"
716
+ [ Elm.Annotation.var "data"
717
+ ]
718
+ )
719
+ )
720
+ , ( "pages"
721
+ , Elm.Annotation.namedWith
722
+ [ "DataSource" ]
723
+ "DataSource"
724
+ [ Elm.Annotation.list (Elm.Annotation.named [] "RouteParams")
725
+ ]
726
+ )
727
+ , ( "head"
728
+ , Elm.Annotation.function
729
+ [ Elm.Annotation.namedWith
730
+ [ "RouteBuilder" ]
731
+ "StaticPayload"
732
+ [ Elm.Annotation.var "data"
733
+ , Elm.Annotation.var "action"
734
+ , Elm.Annotation.var "routeParams"
735
+ ]
736
+ ]
737
+ (Elm.Annotation.list
738
+ (Elm.Annotation.namedWith [ "Head" ] "Tag" [])
739
+ )
740
+ )
741
+ ]
742
+ ]
743
+ (Elm.Annotation.namedWith
744
+ [ "RouteBuilder" ]
745
+ "Builder"
746
+ [ Elm.Annotation.named [] "RouteParams"
747
+ , Elm.Annotation.named [] "Data"
748
+ , Elm.Annotation.named [] "Action"
749
+ ]
750
+ )
751
+ )
752
+ }
753
+ )
754
+ [ Elm.record
755
+ [ Tuple.pair "data" (Elm.functionReduced "serverRenderUnpack" serverRenderArg.data)
756
+ , Tuple.pair "pages" serverRenderArg.pages
757
+ , Tuple.pair "head" (Elm.functionReduced "serverRenderUnpack" serverRenderArg.head)
758
+ ]
759
+ ]
760
+
761
+
762
+ single_ :
763
+ { data : Elm.Expression
764
+ , head : Elm.Expression -> Elm.Expression
765
+ }
766
+ -> Elm.Expression
767
+ single_ serverRenderArg =
768
+ Elm.apply
769
+ (Elm.value
770
+ { importFrom = [ "RouteBuilder" ]
771
+ , name = "single"
772
+ , annotation =
773
+ Just
774
+ (Elm.Annotation.function
775
+ [ Elm.Annotation.record
776
+ [ ( "data"
777
+ , Elm.Annotation.namedWith
778
+ [ "DataSource" ]
779
+ "DataSource"
780
+ [ Elm.Annotation.var "data"
781
+ ]
782
+ )
783
+ , ( "head"
784
+ , Elm.Annotation.function
785
+ [ Elm.Annotation.namedWith
786
+ [ "RouteBuilder" ]
787
+ "StaticPayload"
788
+ [ Elm.Annotation.var "data"
789
+ , Elm.Annotation.var "action"
790
+ , Elm.Annotation.var "routeParams"
791
+ ]
792
+ ]
793
+ (Elm.Annotation.list
794
+ (Elm.Annotation.namedWith [ "Head" ] "Tag" [])
795
+ )
796
+ )
797
+ ]
798
+ ]
799
+ (Elm.Annotation.namedWith
800
+ [ "RouteBuilder" ]
801
+ "Builder"
802
+ [ Elm.Annotation.named [] "RouteParams"
803
+ , Elm.Annotation.named [] "Data"
804
+ , Elm.Annotation.named [] "Action"
805
+ ]
806
+ )
807
+ )
808
+ }
809
+ )
810
+ [ Elm.record
811
+ [ Tuple.pair "data" serverRenderArg.data
812
+ , Tuple.pair "head" (Elm.functionReduced "serverRenderUnpack" serverRenderArg.head)
813
+ ]
814
+ ]
815
+
816
+
466
817
  buildWithLocalState_ :
467
818
  { view :
468
819
  Elm.Expression
@@ -423,9 +423,7 @@ update config appMsg model =
423
423
  )
424
424
 
425
425
  else
426
- ( { model
427
- | url = url
428
- }
426
+ ( model
429
427
  , NoEffect
430
428
  )
431
429
  -- TODO is it reasonable to always re-fetch route data if you re-navigate to the current route? Might be a good
@@ -605,27 +603,32 @@ update config appMsg model =
605
603
  , actionData = newActionData
606
604
  }
607
605
 
608
- ( userModel, _ ) =
606
+ ( userModel, userEffect ) =
609
607
  -- TODO if urlWithoutRedirectResolution is different from the url with redirect resolution, then
610
608
  -- instead of calling update, call pushUrl (I think?)
611
609
  -- TODO include user Cmd
612
- config.update model.pageFormState
613
- (model.inFlightFetchers |> toFetcherState)
614
- (model.transition |> Maybe.map Tuple.second)
615
- newSharedData
616
- newPageData
617
- model.key
618
- (config.onPageChange
619
- { protocol = model.url.protocol
620
- , host = model.url.host
621
- , port_ = model.url.port_
622
- , path = urlPathToPath urlWithoutRedirectResolution
623
- , query = urlWithoutRedirectResolution.query
624
- , fragment = urlWithoutRedirectResolution.fragment
625
- , metadata = config.urlToRoute urlWithoutRedirectResolution
626
- }
627
- )
628
- previousPageData.userModel
610
+ if stayingOnSamePath then
611
+ ( previousPageData.userModel, NoEffect )
612
+
613
+ else
614
+ config.update model.pageFormState
615
+ (model.inFlightFetchers |> toFetcherState)
616
+ (model.transition |> Maybe.map Tuple.second)
617
+ newSharedData
618
+ newPageData
619
+ model.key
620
+ (config.onPageChange
621
+ { protocol = model.url.protocol
622
+ , host = model.url.host
623
+ , port_ = model.url.port_
624
+ , path = urlPathToPath urlWithoutRedirectResolution
625
+ , query = urlWithoutRedirectResolution.query
626
+ , fragment = urlWithoutRedirectResolution.fragment
627
+ , metadata = config.urlToRoute urlWithoutRedirectResolution
628
+ }
629
+ )
630
+ previousPageData.userModel
631
+ |> Tuple.mapSecond UserCmd
629
632
 
630
633
  updatedModel : Model userModel pageData actionData sharedData
631
634
  updatedModel =
@@ -656,10 +659,13 @@ update config appMsg model =
656
659
  , currentPath = newUrl.path
657
660
  }
658
661
  , if not stayingOnSamePath && scrollToTopWhenDone then
659
- ScrollToTop
662
+ Batch
663
+ [ ScrollToTop
664
+ , userEffect
665
+ ]
660
666
 
661
667
  else
662
- NoEffect
668
+ userEffect
663
669
  )
664
670
  |> (case maybeUserMsg of
665
671
  Just userMsg ->
@@ -1392,7 +1398,8 @@ loadDataAndUpdateUrl ( newPageData, newSharedData, newActionData ) maybeUserMsg
1392
1398
  , actionData = newActionData
1393
1399
  }
1394
1400
 
1395
- ( userModel, _ ) =
1401
+ -- TODO use userEffect here?
1402
+ ( userModel, userEffect ) =
1396
1403
  -- TODO if urlWithoutRedirectResolution is different from the url with redirect resolution, then
1397
1404
  -- instead of calling update, call pushUrl (I think?)
1398
1405
  -- TODO include user Cmd