tosijs-ui 1.6.21 → 1.7.0-beta.1

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.
@@ -1,5 +1,8 @@
1
1
  import { elements } from 'tosijs';
2
- import { rewriteImports, AsyncFunction, contextVarName } from './code-transform';
2
+ import { rewriteImports, AsyncFunction, contextVarName, extractTopLevelBindingNames, buildScopeCapture, } from './code-transform';
3
+ // Injected context name for the scope-capture callback (see `onScope`). Chosen to
4
+ // not collide with anything an example would plausibly declare.
5
+ const SCOPE_CAPTURE_VAR = '__tosiCaptureScope';
3
6
  const { div } = elements;
4
7
  /**
5
8
  * Register web components in an iframe's customElements registry.
@@ -48,11 +51,27 @@ export function registerComponentsInIframe(iframeWindow, context) {
48
51
  }
49
52
  }
50
53
  }
54
+ /**
55
+ * Append a scope-capture epilogue to already-transformed example code when a
56
+ * consumer wants the run's locals. Returns the (possibly unchanged) code plus the
57
+ * extra context entry to inject. The epilogue no-ops if the example binds nothing.
58
+ */
59
+ export function withScopeCapture(transformedCode, onScope) {
60
+ if (!onScope)
61
+ return { code: transformedCode, extraContext: {} };
62
+ const epilogue = buildScopeCapture(extractTopLevelBindingNames(transformedCode), SCOPE_CAPTURE_VAR);
63
+ if (!epilogue)
64
+ return { code: transformedCode, extraContext: {} };
65
+ return {
66
+ code: transformedCode + epilogue,
67
+ extraContext: { [SCOPE_CAPTURE_VAR]: onScope },
68
+ };
69
+ }
51
70
  /**
52
71
  * Execute code inline (directly in the page)
53
72
  */
54
73
  export async function executeInline(options) {
55
- const { html, css, js, context, transform, exampleElement, styleElement, widgetsElement, onError, } = options;
74
+ const { html, css, js, context, transform, exampleElement, styleElement, widgetsElement, onError, onScope, } = options;
56
75
  const preview = div({ class: 'preview' });
57
76
  preview.innerHTML = html;
58
77
  styleElement.innerText = css;
@@ -63,14 +82,15 @@ export async function executeInline(options) {
63
82
  else {
64
83
  exampleElement.insertBefore(preview, widgetsElement);
65
84
  }
66
- const fullContext = { preview, ...context };
67
85
  try {
68
86
  const code = rewriteImports(js, Object.keys(context));
69
87
  const transformedCode = (await transform(code, { transforms: ['typescript'] })).code;
88
+ const { code: finalCode, extraContext } = withScopeCapture(transformedCode, onScope);
89
+ const fullContext = { preview, ...context, ...extraContext };
70
90
  const contextKeys = Object.keys(fullContext).map(contextVarName);
71
91
  const contextValues = Object.values(fullContext);
72
92
  // @ts-expect-error AsyncFunction constructor typing
73
- const func = new AsyncFunction(...contextKeys, transformedCode);
93
+ const func = new AsyncFunction(...contextKeys, finalCode);
74
94
  await func(...contextValues);
75
95
  }
76
96
  catch (e) {
@@ -87,7 +107,7 @@ export async function executeInline(options) {
87
107
  * Execute code in an isolated iframe
88
108
  */
89
109
  export async function executeInIframe(options) {
90
- const { html, css, js, context, transform, exampleElement, widgetsElement, onError, } = options;
110
+ const { html, css, js, context, transform, exampleElement, widgetsElement, onError, onScope, } = options;
91
111
  // Create or reuse iframe
92
112
  let iframe = exampleElement.querySelector('iframe.preview-iframe');
93
113
  if (!iframe) {
@@ -138,16 +158,17 @@ export async function executeInIframe(options) {
138
158
  console.error('Could not find preview element in iframe');
139
159
  return null;
140
160
  }
141
- // Execute JS in iframe context
142
- const fullContext = { preview, ...context };
143
161
  try {
144
162
  const code = rewriteImports(js, Object.keys(context));
145
163
  const transformedCode = (await transform(code, { transforms: ['typescript'] })).code;
164
+ const { code: finalCode, extraContext } = withScopeCapture(transformedCode, onScope);
165
+ // Execute JS in iframe context
166
+ const fullContext = { preview, ...context, ...extraContext };
146
167
  // Create AsyncFunction in iframe's context
147
168
  const IframeAsyncFunction = iframeWindow.eval('(async () => {}).constructor');
148
169
  const contextKeys = Object.keys(fullContext).map(contextVarName);
149
170
  const contextValues = Object.values(fullContext);
150
- const func = new IframeAsyncFunction(...contextKeys, transformedCode);
171
+ const func = new IframeAsyncFunction(...contextKeys, finalCode);
151
172
  await func(...contextValues);
152
173
  }
153
174
  catch (e) {