tungsten 0.3.1 → 0.3.2

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 (103) hide show
  1. package/dist/Components.d.ts +2 -1
  2. package/dist/Components.d.ts.map +1 -1
  3. package/dist/Components.js +10 -1
  4. package/dist/DomRegion.js +1 -1
  5. package/dist/FrameworkConfig.d.ts +4 -0
  6. package/dist/FrameworkConfig.d.ts.map +1 -1
  7. package/dist/FunctionComponentStandIn.d.ts +2 -0
  8. package/dist/FunctionComponentStandIn.d.ts.map +1 -1
  9. package/dist/FunctionComponentStandIn.js +10 -1
  10. package/dist/FunctionComponentStandIn.sxyt.d.ts +2 -0
  11. package/dist/FunctionComponentStandIn.sxyt.d.ts.map +1 -0
  12. package/dist/FunctionComponentStandIn.sxyt.js +52 -0
  13. package/dist/Servers/DevServer.d.ts +2 -0
  14. package/dist/Servers/DevServer.d.ts.map +1 -1
  15. package/dist/Servers/DevServer.js +32 -5
  16. package/dist/Servers/devErrorPageAutoRetry.d.ts +4 -0
  17. package/dist/Servers/devErrorPageAutoRetry.d.ts.map +1 -0
  18. package/dist/Servers/devErrorPageAutoRetry.js +26 -0
  19. package/dist/Servers/devErrorPageAutoRetry.sxyt.d.ts +2 -0
  20. package/dist/Servers/devErrorPageAutoRetry.sxyt.d.ts.map +1 -0
  21. package/dist/Servers/devErrorPageAutoRetry.sxyt.js +117 -0
  22. package/dist/api/getExport.d.ts.map +1 -1
  23. package/dist/api/getExport.js +2 -1
  24. package/dist/babelPlugins/BabelAddDevCacheBust.d.ts +11 -0
  25. package/dist/babelPlugins/BabelAddDevCacheBust.d.ts.map +1 -0
  26. package/dist/babelPlugins/BabelAddDevCacheBust.js +68 -0
  27. package/dist/babelPlugins/BabelAddDevCacheBust.sxyt.d.ts +2 -0
  28. package/dist/babelPlugins/BabelAddDevCacheBust.sxyt.d.ts.map +1 -0
  29. package/dist/babelPlugins/BabelAddDevCacheBust.sxyt.js +138 -0
  30. package/dist/bin/commands/dev.d.ts.map +1 -1
  31. package/dist/bin/commands/dev.js +2 -0
  32. package/dist/build/compileUserApp.js +2 -2
  33. package/dist/build/makeBabelUserDevConfig.d.ts.map +1 -1
  34. package/dist/build/makeBabelUserDevConfig.js +6 -1
  35. package/dist/defaultConfig.d.ts +2 -0
  36. package/dist/defaultConfig.d.ts.map +1 -1
  37. package/dist/defaultConfig.js +3 -1
  38. package/dist/env/getEnvSchemas.d.ts.map +1 -1
  39. package/dist/env/getEnvSchemas.js +2 -1
  40. package/dist/getComponent.d.ts.map +1 -1
  41. package/dist/getComponent.js +2 -1
  42. package/dist/getRoutes.d.ts.map +1 -1
  43. package/dist/getRoutes.js +2 -1
  44. package/dist/hmr/applyHmrModule.d.ts +11 -0
  45. package/dist/hmr/applyHmrModule.d.ts.map +1 -0
  46. package/dist/hmr/applyHmrModule.js +78 -0
  47. package/dist/hmr/applyHmrModule.sxyt.d.ts +2 -0
  48. package/dist/hmr/applyHmrModule.sxyt.d.ts.map +1 -0
  49. package/dist/hmr/applyHmrModule.sxyt.js +182 -0
  50. package/dist/hmr/componentIdentity.d.ts +9 -0
  51. package/dist/hmr/componentIdentity.d.ts.map +1 -0
  52. package/dist/hmr/componentIdentity.js +17 -0
  53. package/dist/hmr/instanceRegistry.d.ts +4 -0
  54. package/dist/hmr/instanceRegistry.d.ts.map +1 -0
  55. package/dist/hmr/instanceRegistry.js +6 -0
  56. package/dist/hmr/instanceRegistry.sxyt.d.ts +2 -0
  57. package/dist/hmr/instanceRegistry.sxyt.d.ts.map +1 -0
  58. package/dist/hmr/instanceRegistry.sxyt.js +24 -0
  59. package/dist/hmr/liveReloadHub.d.ts +10 -0
  60. package/dist/hmr/liveReloadHub.d.ts.map +1 -0
  61. package/dist/{dev → hmr}/liveReloadHub.js +8 -2
  62. package/dist/hmr/refreshStylesheets.d.ts +2 -0
  63. package/dist/hmr/refreshStylesheets.d.ts.map +1 -0
  64. package/dist/hmr/refreshStylesheets.js +26 -0
  65. package/dist/hmr/refreshStylesheets.sxyt.d.ts +2 -0
  66. package/dist/hmr/refreshStylesheets.sxyt.d.ts.map +1 -0
  67. package/dist/hmr/refreshStylesheets.sxyt.js +46 -0
  68. package/dist/hydrate/hydrate.d.ts +9 -1
  69. package/dist/hydrate/hydrate.d.ts.map +1 -1
  70. package/dist/hydrate/hydrate.js +31 -7
  71. package/dist/hydrate/hydrate.sxyt.d.ts +2 -0
  72. package/dist/hydrate/hydrate.sxyt.d.ts.map +1 -0
  73. package/dist/hydrate/hydrate.sxyt.js +302 -0
  74. package/dist/hydrate/reconcileHydratedAttributes.d.ts.map +1 -1
  75. package/dist/hydrate/reconcileHydratedAttributes.js +10 -2
  76. package/dist/hydrate/reconcileHydratedAttributes.sxyt.js +17 -0
  77. package/dist/importDevModuleWithRetry.d.ts +3 -0
  78. package/dist/importDevModuleWithRetry.d.ts.map +1 -0
  79. package/dist/importDevModuleWithRetry.js +46 -0
  80. package/dist/importDevModuleWithRetry.sxyt.d.ts +2 -0
  81. package/dist/importDevModuleWithRetry.sxyt.d.ts.map +1 -0
  82. package/dist/importDevModuleWithRetry.sxyt.js +104 -0
  83. package/dist/render/HeadData.d.ts +1 -0
  84. package/dist/render/HeadData.d.ts.map +1 -1
  85. package/dist/render/contentToHtml.d.ts.map +1 -1
  86. package/dist/render/contentToHtml.js +2 -1
  87. package/dist/render/processHead.d.ts.map +1 -1
  88. package/dist/render/processHead.js +47 -6
  89. package/dist/render/processHead.sxyt.d.ts +2 -0
  90. package/dist/render/processHead.sxyt.d.ts.map +1 -0
  91. package/dist/render/processHead.sxyt.js +93 -0
  92. package/dist/update/hotSwap.sxyt.d.ts +2 -0
  93. package/dist/update/hotSwap.sxyt.d.ts.map +1 -0
  94. package/dist/update/hotSwap.sxyt.js +252 -0
  95. package/dist/update/integrateUpdateVdom.d.ts +5 -1
  96. package/dist/update/integrateUpdateVdom.d.ts.map +1 -1
  97. package/dist/update/integrateUpdateVdom.js +107 -4
  98. package/dist/warnOnFrameworkChange.d.ts +2 -0
  99. package/dist/warnOnFrameworkChange.d.ts.map +1 -0
  100. package/dist/warnOnFrameworkChange.js +27 -0
  101. package/package.json +1 -1
  102. package/dist/dev/liveReloadHub.d.ts +0 -6
  103. package/dist/dev/liveReloadHub.d.ts.map +0 -1
@@ -0,0 +1,68 @@
1
+ // BabelAddDevCacheBust
2
+ // exports MakeBabelAddDevCacheBust (function)
3
+ //
4
+ // Dev-only. Appends a `?v=<mtime>` query to every relative import/re-export
5
+ // specifier, versioned off the imported file's own mtime as it sits in
6
+ // codeLocation (the tsc-emitted, pre-final-babel intermediate directory).
7
+ //
8
+ // Without this, the client's HMR reload script only cache-busts the
9
+ // top-level file(s) named in the SSE payload (`?t=<now>`); anything THAT
10
+ // file statically imports by its plain URL still resolves against whatever
11
+ // the browser had cached for that plain URL. On a rename that touches two
12
+ // files in the same edit (e.g. `Button.tsx` renaming an export while
13
+ // `Home.tsx` updates its import of it), the freshly re-fetched `Home.js`'s
14
+ // own static `import ... from '../components/Button.js'` could resolve to a
15
+ // stale cached `Button.js` that doesn't have the new export yet, throwing a
16
+ // genuine browser SyntaxError before HMR ever gets a chance to run - see
17
+ // docs/hmr.md's "stale-module-cache SyntaxError" finding. Stamping every
18
+ // import with the target's current mtime means an edited dependency always
19
+ // gets a URL the browser has never cached, regardless of which file in the
20
+ // chain was named in the SSE event.
21
+ //
22
+ // Must run after BabelTransformJsxExtensionsToJs (registered later in the
23
+ // dev plugin list) so it seess the corrected '.js' specifier - but resolution
24
+ // tries both '.js' and '.jsx' on disk regardless, since codeLocation only
25
+ // ever contains the pre-rename '.jsx' file, so this works whichever order
26
+ // plugins actually end up running in.
27
+ import { dirname, join } from 'path';
28
+ import fs from 'fs';
29
+ function resolveOnDiskMtime(fromDir, specifier) {
30
+ if (!specifier.startsWith('.')) return undefined;
31
+ const candidates = specifier.endsWith('.jsx') ? [specifier, specifier.slice(0, -1)] : specifier.endsWith('.js') ? [specifier, `${specifier.slice(0, -3)}.jsx`] : [];
32
+ for (const candidate of candidates) {
33
+ const abs = join(fromDir, candidate);
34
+ if (fs.existsSync(abs)) return fs.statSync(abs).mtimeMs;
35
+ }
36
+ return undefined;
37
+ }
38
+ function versionedSpecifier(fromDir, specifier) {
39
+ const mtimeMs = resolveOnDiskMtime(fromDir, specifier);
40
+ if (mtimeMs === undefined) return specifier;
41
+ const separator = specifier.includes('?') ? '&' : '?';
42
+ return `${specifier}${separator}v=${mtimeMs}`;
43
+ }
44
+ export function MakeBabelAddDevCacheBust() {
45
+ return function BabelAddDevCacheBust() {
46
+ let fileDir = '';
47
+ return {
48
+ pre(state) {
49
+ const stateFilename = state.opts.filename;
50
+ fileDir = typeof stateFilename === 'string' ? dirname(stateFilename) : '';
51
+ },
52
+ visitor: {
53
+ ImportDeclaration(path) {
54
+ if (fileDir === '') return;
55
+ path.node.source.value = versionedSpecifier(fileDir, path.node.source.value);
56
+ },
57
+ ExportNamedDeclaration(path) {
58
+ if (fileDir === '' || path.node.source == null) return;
59
+ path.node.source.value = versionedSpecifier(fileDir, path.node.source.value);
60
+ },
61
+ ExportAllDeclaration(path) {
62
+ if (fileDir === '') return;
63
+ path.node.source.value = versionedSpecifier(fileDir, path.node.source.value);
64
+ }
65
+ }
66
+ };
67
+ };
68
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=BabelAddDevCacheBust.sxyt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BabelAddDevCacheBust.sxyt.d.ts","sourceRoot":"","sources":["../../src/babelPlugins/BabelAddDevCacheBust.sxyt.ts"],"names":[],"mappings":""}
@@ -0,0 +1,138 @@
1
+ import { expect } from 'chai';
2
+ import { describe } from 'sxy-test-runner';
3
+ import { transformSync } from '@babel/core';
4
+ import { mkdir, mkdtemp, rm, writeFile, utimes } from 'fs/promises';
5
+ import { statSync, readFileSync } from 'fs';
6
+ import { tmpdir } from 'os';
7
+ import { join } from 'path';
8
+ import { MakeBabelAddDevCacheBust } from "./BabelAddDevCacheBust.js";
9
+ async function makeFixture() {
10
+ const dir = await mkdtemp(join(tmpdir(), 'tungsten-add-dev-cache-bust-'));
11
+ const components = join(dir, 'components');
12
+ const pages = join(dir, 'pages');
13
+ await mkdir(components, {
14
+ recursive: true
15
+ });
16
+ await mkdir(pages, {
17
+ recursive: true
18
+ });
19
+ const buttonPath = join(components, 'Button.jsx');
20
+ await writeFile(buttonPath, `export function Button() { return null }\n`);
21
+ const homePath = join(pages, 'Home.jsx');
22
+ await writeFile(homePath, `import { Button } from '../components/Button.jsx'\nexport const x = Button\n`);
23
+ return {
24
+ dir,
25
+ buttonPath,
26
+ homePath,
27
+ components,
28
+ pages
29
+ };
30
+ }
31
+ function transform(filename) {
32
+ const result = transformSync(readFileSync(filename, 'utf8'), {
33
+ filename,
34
+ babelrc: false,
35
+ configFile: false,
36
+ plugins: [MakeBabelAddDevCacheBust()]
37
+ });
38
+ if (result === null || typeof result.code !== 'string') throw new Error('babel transform returned no code');
39
+ return result.code;
40
+ }
41
+ describe('BabelAddDevCacheBust', ({
42
+ it
43
+ }) => {
44
+ it(`appends the imported file's on-disk mtime as a '?v=' query to a relative import`, async () => {
45
+ const {
46
+ dir,
47
+ buttonPath,
48
+ homePath
49
+ } = await makeFixture();
50
+ try {
51
+ const code = transform(homePath);
52
+ const mtimeMs = statSync(buttonPath).mtimeMs;
53
+ expect(code).to.contain(`../components/Button.jsx?v=${mtimeMs}`);
54
+ } finally {
55
+ await rm(dir, {
56
+ recursive: true,
57
+ force: true
58
+ });
59
+ }
60
+ });
61
+ it(`produces a different '?v=' value once the imported file is edited - so a fresh dependent refetch resolves a URL the browser has never cached`, async () => {
62
+ const {
63
+ dir,
64
+ buttonPath,
65
+ homePath
66
+ } = await makeFixture();
67
+ try {
68
+ const before = transform(homePath);
69
+ const beforeMatch = before.match(/\?v=([0-9.]+)["']/);
70
+ expect(beforeMatch).to.not.equal(null);
71
+ // simulate the developer editing Button.jsx a moment later
72
+ const future = new Date(Date.now() + 5000);
73
+ await utimes(buttonPath, future, future);
74
+ const after = transform(homePath);
75
+ const afterMatch = after.match(/\?v=([0-9.]+)["']/);
76
+ expect(afterMatch).to.not.equal(null);
77
+ expect(afterMatch[1]).to.not.equal(beforeMatch[1]);
78
+ } finally {
79
+ await rm(dir, {
80
+ recursive: true,
81
+ force: true
82
+ });
83
+ }
84
+ });
85
+ it(`resolves correctly even when the specifier's extension has already been rewritten from .jsx to .js by an earlier plugin`, async () => {
86
+ const {
87
+ dir,
88
+ buttonPath,
89
+ pages
90
+ } = await makeFixture();
91
+ try {
92
+ const homeAlreadyJsPath = join(pages, 'HomeAlreadyJs.jsx');
93
+ await writeFile(homeAlreadyJsPath, `import { Button } from '../components/Button.js'\nexport const x = Button\n`);
94
+ const code = transform(homeAlreadyJsPath);
95
+ const mtimeMs = statSync(buttonPath).mtimeMs;
96
+ expect(code).to.contain(`../components/Button.js?v=${mtimeMs}`);
97
+ } finally {
98
+ await rm(dir, {
99
+ recursive: true,
100
+ force: true
101
+ });
102
+ }
103
+ });
104
+ it(`leaves a bare (package) import untouched`, async () => {
105
+ const {
106
+ dir,
107
+ homePath
108
+ } = await makeFixture();
109
+ try {
110
+ await writeFile(homePath, `import { StatefulComponent } from 'tungsten'\nexport const x = StatefulComponent\n`);
111
+ const code = transform(homePath);
112
+ expect(code).to.contain(`tungsten`);
113
+ expect(code).to.not.contain(`?v=`);
114
+ } finally {
115
+ await rm(dir, {
116
+ recursive: true,
117
+ force: true
118
+ });
119
+ }
120
+ });
121
+ it(`leaves a relative import untouched when the target file cannot be found on disk`, async () => {
122
+ const {
123
+ dir,
124
+ homePath
125
+ } = await makeFixture();
126
+ try {
127
+ await writeFile(homePath, `import { Ghost } from '../components/Ghost.jsx'\nexport const x = Ghost\n`);
128
+ const code = transform(homePath);
129
+ expect(code).to.contain(`../components/Ghost.jsx`);
130
+ expect(code).to.not.contain(`?v=`);
131
+ } finally {
132
+ await rm(dir, {
133
+ recursive: true,
134
+ force: true
135
+ });
136
+ }
137
+ });
138
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/bin/commands/dev.ts"],"names":[],"mappings":"AAwCA,OAAO,EAAE,CAAA"}
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/bin/commands/dev.ts"],"names":[],"mappings":"AA2CA,OAAO,EAAE,CAAA"}
@@ -1,6 +1,7 @@
1
1
  import open from 'open';
2
2
  import { cliOut } from "../../cliOutput.js";
3
3
  import { Framework } from "../../Framework.js";
4
+ import { warnOnFrameworkChange } from "../../warnOnFrameworkChange.js";
4
5
  //import dotenv from 'dotenv'
5
6
  //dotenv.config({ path: '.private.env' })
6
7
  cliOut(`Starting dev mode`);
@@ -17,6 +18,7 @@ await sxyFramework.build('dev');
17
18
  const devServer = await sxyFramework.createDevServer();
18
19
  //cliOut('Dev server ready')
19
20
  const devServerLocation = await devServer.start();
21
+ if (sxyFramework.config?.warnOnFrameworkChange) warnOnFrameworkChange();
20
22
  // do a dummy request to trigger any error immediately during dev
21
23
  //axios.get(sxyFramework.devServer.location)
22
24
  cliOut('Booting to the dev site in your browser');
@@ -5,7 +5,7 @@ import { copyAssets } from "./copyAssets.js";
5
5
  import { copyPublicFiles } from "./copyPublicFiles.js";
6
6
  import { buildCodeSubPath, bundleFolder, devBuildOuterFolder, prodBuildFolder } from "../settings.js";
7
7
  import { combineComponentCss } from "./combineComponentCss.js";
8
- import { emitReloadEvent } from "../dev/liveReloadHub.js";
8
+ import { emitReloadEvent } from "../hmr/liveReloadHub.js";
9
9
  //import { compileEnv } from './compileEnv.js'
10
10
  export async function compileUserApp(projectDir, appConfig, mode) {
11
11
  const codeLocation = join(projectDir, appConfig.codeDir);
@@ -17,7 +17,7 @@ export async function compileUserApp(projectDir, appConfig, mode) {
17
17
  const buildCodeLocation = join(buildLocation, codeSubPath);
18
18
  const compileJavascriptPromise = compileJavascript(projectDir, appConfig, codeLocation, sourceLocation, styleImportBase, buildLocation, buildCodeLocation, mode, true, mode === 'dev' ? {
19
19
  onCycleComplete: cycle => {
20
- if (cycle.success) emitReloadEvent('js');
20
+ if (cycle.success) emitReloadEvent('js', cycle.file);
21
21
  }
22
22
  } : {});
23
23
  //const compileEnvPromise = compileEnv(projectDir, appConfig, codeLocation, buildLocation, mode)
@@ -1 +1 @@
1
- {"version":3,"file":"makeBabelUserDevConfig.d.ts","sourceRoot":"","sources":["../../src/build/makeBabelUserDevConfig.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAKnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAEvD,OAAO,EAAoC,eAAe,EAAE,MAAM,iDAAiD,CAAA;AAMnH,wBAAgB,sBAAsB,CAClC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,kBAAkB,EAAE,MAAM,EAC1B,aAAa,EAAE,MAAM,EACrB,iBAAiB,EAAE,MAAM,EACzB,qBAAqB,EAAE,MAAM,EAC7B,2BAA2B,EAAE,WAAW,CAAC,MAAM,CAAC,EAChD,kBAAkB,EAAE,eAAe,EACnC,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,GACzB,gBAAgB,CA4BlB"}
1
+ {"version":3,"file":"makeBabelUserDevConfig.d.ts","sourceRoot":"","sources":["../../src/build/makeBabelUserDevConfig.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAKnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAEvD,OAAO,EAAoC,eAAe,EAAE,MAAM,iDAAiD,CAAA;AAOnH,wBAAgB,sBAAsB,CAClC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,kBAAkB,EAAE,MAAM,EAC1B,aAAa,EAAE,MAAM,EACrB,iBAAiB,EAAE,MAAM,EACzB,qBAAqB,EAAE,MAAM,EAC7B,2BAA2B,EAAE,WAAW,CAAC,MAAM,CAAC,EAChD,kBAAkB,EAAE,eAAe,EACnC,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,GACzB,gBAAgB,CAgClB"}
@@ -6,6 +6,7 @@ import { MakeBabelTransformPackageImports } from "../babelPlugins/BabelTransform
6
6
  import { componentCssFolder, componentCssFile } from "../settings.js";
7
7
  import userCommonConfig from "./babel.user.common.config.js";
8
8
  import { MakeBabelTransformCssImports } from "../babelPlugins/BabelTransformCssImports.js";
9
+ import { MakeBabelAddDevCacheBust } from "../babelPlugins/BabelAddDevCacheBust.js";
9
10
  export function makeBabelUserDevConfig(projectDir, absCodeDir, absSourceDir, absStyleImportBase, buildLocation, buildCodeLocation, packagesCacheLocation, skipPackageCompilationFiles, oldDependencyGraph, dependencyGraph, importAliases, promises) {
10
11
  const userDevConfig = objectCopy(userCommonConfig);
11
12
  userDevConfig.plugins.push(MakeBabelTransformPackageImports({
@@ -29,6 +30,10 @@ export function makeBabelUserDevConfig(projectDir, absCodeDir, absSourceDir, abs
29
30
  projectAbsStyleImportDir: absStyleImportBase,
30
31
  classNameFormat: '[folder]-[name]_[local]',
31
32
  cssOutputFile: join(buildLocation, componentCssFolder, componentCssFile)
32
- }));
33
+ }),
34
+ // Dev-only: keeps every relative import's browser-cached URL in sync with the
35
+ // imported file's own on-disk mtime - see BabelAddDevCacheBust.ts and
36
+ // docs/hmr.md's "stale-module-cache SyntaxError" finding.
37
+ MakeBabelAddDevCacheBust());
33
38
  return userDevConfig;
34
39
  }
@@ -15,6 +15,8 @@ export declare const defaultConfig: {
15
15
  apiPath: string;
16
16
  suppressNoHeadWarning: false;
17
17
  suppressOriginMismatchWarning: false;
18
+ warnOnFrameworkChange: true;
19
+ hmr: true;
18
20
  };
19
21
  export type FrameworkConfigDefault = typeof defaultConfig;
20
22
  //# sourceMappingURL=defaultConfig.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"defaultConfig.d.ts","sourceRoot":"","sources":["../src/defaultConfig.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;CAqBS,CAAA;AAEnC,MAAM,MAAM,sBAAsB,GAAG,OAAO,aAAa,CAAA"}
1
+ {"version":3,"file":"defaultConfig.d.ts","sourceRoot":"","sources":["../src/defaultConfig.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;CAuBS,CAAA;AAEnC,MAAM,MAAM,sBAAsB,GAAG,OAAO,aAAa,CAAA"}
@@ -20,5 +20,7 @@ export const defaultConfig = {
20
20
  apiPath: '/api',
21
21
  //debug: false // moved to env
22
22
  suppressNoHeadWarning: false,
23
- suppressOriginMismatchWarning: false
23
+ suppressOriginMismatchWarning: false,
24
+ warnOnFrameworkChange: true,
25
+ hmr: true
24
26
  };
@@ -1 +1 @@
1
- {"version":3,"file":"getEnvSchemas.d.ts","sourceRoot":"","sources":["../../src/env/getEnvSchemas.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAEpE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AAI7B,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAGxC,MAAM,MAAM,UAAU,GAAG;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC,WAAW,GAAG,kBAAkB,CAAC,CAAC;IAC7D,eAAe,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;CAC1C,CAAA;AAKD,wBAAsB,aAAa,CAAC,MAAM,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,GAChG,OAAO,CAAC,UAAU,CAAC,CAAA;AACrB,wBAAsB,aAAa,CAAC,MAAM,EAAE,uBAAuB,EAAE,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,MAAM,GACjG,OAAO,CAAC,UAAU,CAAC,CAAA;AAoErB,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,uBAGlF"}
1
+ {"version":3,"file":"getEnvSchemas.d.ts","sourceRoot":"","sources":["../../src/env/getEnvSchemas.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAEpE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AAK7B,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAGxC,MAAM,MAAM,UAAU,GAAG;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC,WAAW,GAAG,kBAAkB,CAAC,CAAC;IAC7D,eAAe,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;CAC1C,CAAA;AAKD,wBAAsB,aAAa,CAAC,MAAM,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,GAChG,OAAO,CAAC,UAAU,CAAC,CAAA;AACrB,wBAAsB,aAAa,CAAC,MAAM,EAAE,uBAAuB,EAAE,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,MAAM,GACjG,OAAO,CAAC,UAAU,CAAC,CAAA;AAoErB,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,uBAGlF"}
@@ -2,6 +2,7 @@ import { join } from 'path';
2
2
  import { fixExtension } from "../fixExtension.js";
3
3
  import { throwError } from "../throwError.js";
4
4
  import { iterationQueryString } from "../iterationQueryString.js";
5
+ import { importDevModuleWithRetry } from "../importDevModuleWithRetry.js";
5
6
  export async function getEnvSchemas(config, iteration = null, mode = 'prod') {
6
7
  const {
7
8
  envSchemasLocation
@@ -16,7 +17,7 @@ export async function getEnvSchemas(config, iteration = null, mode = 'prod') {
16
17
  if (iteration === null) throwError('getEnvSchemas()', 'iteration is null');
17
18
  //if (!load) throwError('getEnvSchemas()', 'load is null')
18
19
  //module = await load(fullFilePath)
19
- module = await import(iterationQueryString(fullFilePath, iteration));
20
+ module = await importDevModuleWithRetry(iterationQueryString(fullFilePath, iteration));
20
21
  } else {
21
22
  module = await import(fullFilePath);
22
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"getComponent.d.ts","sourceRoot":"","sources":["../src/getComponent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAG7D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAMhC,wBAAsB,YAAY,CAC9B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAEhB,SAAS,GAAE,MAAM,GAAG,IAAW,EAC/B,IAAI,GAAE,IAAa,GACpB,OAAO,CAAC,gBAAgB,CAAC,CA6B3B"}
1
+ {"version":3,"file":"getComponent.d.ts","sourceRoot":"","sources":["../src/getComponent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAG7D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAOhC,wBAAsB,YAAY,CAC9B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAEhB,SAAS,GAAE,MAAM,GAAG,IAAW,EAC/B,IAAI,GAAE,IAAa,GACpB,OAAO,CAAC,gBAAgB,CAAC,CA6B3B"}
@@ -2,6 +2,7 @@ import { fixExtension } from "./fixExtension.js";
2
2
  import { TungstenError } from "./TungstenError.js";
3
3
  import { throwError } from "./throwError.js";
4
4
  import { iterationQueryString } from "./iterationQueryString.js";
5
+ import { importDevModuleWithRetry } from "./importDevModuleWithRetry.js";
5
6
  //let load //// !! future improvement
6
7
  export async function getComponent(filePath, filename,
7
8
  //load: Load | null = null,
@@ -16,7 +17,7 @@ iteration = null, mode = 'prod') {
16
17
  //const load = await createLoad(import.meta.url, sxyLoaderConfig)
17
18
  //console.log('load', filePath)
18
19
  //module = await load(filePath)
19
- module = await import(iterationQueryString(filePath, iteration));
20
+ module = await importDevModuleWithRetry(iterationQueryString(filePath, iteration));
20
21
  } else {
21
22
  //console.log('import', filePath)
22
23
  module = await import(filePath); // import the module
@@ -1 +1 @@
1
- {"version":3,"file":"getRoutes.d.ts","sourceRoot":"","sources":["../src/getRoutes.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAGrC,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AAE9D,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAK5D,wBAAsB,SAAS,CAC3B,eAAe,EAAE,eAAe,EAChC,MAAM,EAAE,uBAAuB,EAE/B,SAAS,GAAE,MAAM,GAAG,IAAW,EAC/B,IAAI,GAAE,IAAa,GACnB,OAAO,CAAC,SAAS,CAAC,CAmCrB"}
1
+ {"version":3,"file":"getRoutes.d.ts","sourceRoot":"","sources":["../src/getRoutes.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAGrC,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AAE9D,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAM5D,wBAAsB,SAAS,CAC3B,eAAe,EAAE,eAAe,EAChC,MAAM,EAAE,uBAAuB,EAE/B,SAAS,GAAE,MAAM,GAAG,IAAW,EAC/B,IAAI,GAAE,IAAa,GACnB,OAAO,CAAC,SAAS,CAAC,CAmCrB"}
package/dist/getRoutes.js CHANGED
@@ -3,6 +3,7 @@ import { fixExtension } from "./fixExtension.js";
3
3
  import { TungstenError } from "./TungstenError.js";
4
4
  import { iterationQueryString } from "./iterationQueryString.js";
5
5
  import { throwError } from "./throwError.js";
6
+ import { importDevModuleWithRetry } from "./importDevModuleWithRetry.js";
6
7
  export async function getRoutes(compilationInfo, config,
7
8
  //load: Load | null = null,
8
9
  iteration = null, mode = 'prod') {
@@ -22,7 +23,7 @@ iteration = null, mode = 'prod') {
22
23
  // can we share load across the render?
23
24
  //const load = await createLoad(import.meta.url, sxyLoaderConfig)
24
25
  //module = await load(filePath)
25
- module = await import(iterationQueryString(filePath, iteration));
26
+ module = await importDevModuleWithRetry(iterationQueryString(filePath, iteration));
26
27
  } else {
27
28
  module = await import(filePath); // import the module
28
29
  }
@@ -0,0 +1,11 @@
1
+ export { refreshStylesheets } from './refreshStylesheets.js';
2
+ export type ApplyHmrModuleResult = {
3
+ matched: true;
4
+ count: number;
5
+ orphaned: boolean;
6
+ } | {
7
+ matched: false;
8
+ orphaned: boolean;
9
+ };
10
+ export declare function applyHmrModule(moduleExports: Record<string, unknown>, file?: string): Promise<ApplyHmrModuleResult>;
11
+ //# sourceMappingURL=applyHmrModule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applyHmrModule.d.ts","sourceRoot":"","sources":["../../src/hmr/applyHmrModule.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAE5D,MAAM,MAAM,oBAAoB,GAC1B;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GACnD;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAA;AAsB3C,wBAAsB,cAAc,CAChC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,GACtD,OAAO,CAAC,oBAAoB,CAAC,CAkD/B"}
@@ -0,0 +1,78 @@
1
+ import { AnyComponentIsFunctionComponentStandIn } from "../AnyComponent.js";
2
+ import { hotSwapClassComponent, hotSwapFunctionComponent } from "../update/integrateUpdateVdom.js";
3
+ import { componentTypeIdentityKey, componentTypeKind, liveComponentIdentityKey, liveComponentKind } from "./componentIdentity.js";
4
+ import { getCurrentInstance } from "./instanceRegistry.js";
5
+ export { refreshStylesheets } from "./refreshStylesheets.js";
6
+ function identityToken(kind, key) {
7
+ return `${kind}:${key}`;
8
+ }
9
+ function tokenKind(token) {
10
+ return token.startsWith('function:') ? 'function' : 'class';
11
+ }
12
+ function tokenKey(token) {
13
+ return token.slice(token.indexOf(':') + 1);
14
+ }
15
+ // Per source file, the set of component identity tokens ('kind:key') it exported the last time
16
+ // this ran. Lets a later edit to the same file detect "this used to export a component that's
17
+ // still live, and doesn't anymore" (a rename or delete) - see tungsten/docs/hmr.md's "orphaned
18
+ // components" note. Cleared naturally on a real page load, since this module state doesn't
19
+ // survive a full reload.
20
+ const previousFileExportIdentities = new Map();
21
+ // Given the freshly re-imported exports of one recompiled file, find every live component
22
+ // instance that came from a same-named, same-kind definition and rebuild it in place. See
23
+ // tungsten/docs/hmr.md for the full design and the reasoning behind the identity key.
24
+ export async function applyHmrModule(moduleExports, file) {
25
+ const instance = getCurrentInstance();
26
+ if (instance === undefined) return {
27
+ matched: false,
28
+ orphaned: false
29
+ };
30
+ let matchedCount = 0;
31
+ const currentIdentities = new Set();
32
+ for (const exportName in moduleExports) {
33
+ const exported = moduleExports[exportName];
34
+ if (typeof exported !== 'function') continue;
35
+ const NewType = exported; // eslint-disable-line no-type-assertion/no-type-assertion -- narrowed by typeof above, definitively a function/class value
36
+ const newKind = componentTypeKind(NewType);
37
+ const newKey = componentTypeIdentityKey(NewType);
38
+ currentIdentities.add(identityToken(newKind, newKey));
39
+ // snapshot: hot-swapping unregisters the old instance and registers its replacement,
40
+ // mutating registeredComponents mid-loop otherwise
41
+ for (const component of [...instance.registeredComponents]) {
42
+ const anyComponent = component; // eslint-disable-line no-type-assertion/no-type-assertion -- every registered component is actually a class instance or FunctionElement stand-in
43
+ if (liveComponentKind(anyComponent) !== newKind) continue;
44
+ if (liveComponentIdentityKey(anyComponent) !== newKey) continue;
45
+ const swapped = AnyComponentIsFunctionComponentStandIn(anyComponent) ? await hotSwapFunctionComponent(anyComponent, NewType) // eslint-disable-line no-type-assertion/no-type-assertion -- newKind === 'function' guaranteed by the kind check above
46
+ : await hotSwapClassComponent(anyComponent, NewType); // eslint-disable-line no-type-assertion/no-type-assertion -- newKind === 'class' guaranteed by the kind check above
47
+ if (swapped !== null) matchedCount++;
48
+ }
49
+ }
50
+ let orphaned = false;
51
+ if (file !== undefined) {
52
+ const previousIdentities = previousFileExportIdentities.get(file);
53
+ if (previousIdentities !== undefined) {
54
+ for (const previousToken of previousIdentities) {
55
+ if (currentIdentities.has(previousToken)) continue;
56
+ const kind = tokenKind(previousToken);
57
+ const key = tokenKey(previousToken);
58
+ const stillLive = [...instance.registeredComponents].some(component => {
59
+ const anyComponent = component; // eslint-disable-line no-type-assertion/no-type-assertion -- every registered component is actually a class instance or FunctionElement stand-in
60
+ return liveComponentKind(anyComponent) === kind && liveComponentIdentityKey(anyComponent) === key;
61
+ });
62
+ if (stillLive) {
63
+ orphaned = true;
64
+ break;
65
+ }
66
+ }
67
+ }
68
+ previousFileExportIdentities.set(file, currentIdentities);
69
+ }
70
+ return matchedCount > 0 ? {
71
+ matched: true,
72
+ count: matchedCount,
73
+ orphaned
74
+ } : {
75
+ matched: false,
76
+ orphaned
77
+ };
78
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=applyHmrModule.sxyt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applyHmrModule.sxyt.d.ts","sourceRoot":"","sources":["../../src/hmr/applyHmrModule.sxyt.tsx"],"names":[],"mappings":""}