Skip to content

Commit

Permalink
Fixes #151
Browse files Browse the repository at this point in the history
  • Loading branch information
zachleat committed Mar 30, 2023
1 parent 78b3681 commit 4294309
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 16 deletions.
10 changes: 5 additions & 5 deletions src/ast.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ class AstSerializer {

// helper functions are used in @html and render functions
// TODO lookup attributes too?
setHelper(name, callback) {
this.dataCascade.setHelper(name, callback);
setHelper(name, callback, isScoped) {
this.dataCascade.setHelper(name, callback, isScoped);
}

setData(data = {}) {
Expand Down Expand Up @@ -430,7 +430,7 @@ class AstSerializer {
}
}

let nodeData = this.dataCascade.getData( options.componentProps, options.hostComponentData, parentComponent?.setupScript, options.injectedData );
let nodeData = this.dataCascade.getData( parentComponent.isTopLevelComponent, options.componentProps, options.hostComponentData, parentComponent?.setupScript, options.injectedData );
let evaluatedAttributes = await AttributeSerializer.evaluateAttributesArray(attrs, nodeData, options.closestParentComponent);
let finalAttributesObject = AttributeSerializer.mergeAttributes(evaluatedAttributes);

Expand Down Expand Up @@ -487,7 +487,7 @@ class AstSerializer {
slotsText.default = this.getPreparsedRawTextContent(o.hostComponentNode, o);
}

let context = this.dataCascade.getData(options.componentProps, options.currentTagAttributes, parentComponent?.setupScript, options.injectedData, {
let context = this.dataCascade.getData(parentComponent.isTopLevelComponent, options.componentProps, options.currentTagAttributes, parentComponent?.setupScript, options.injectedData, {
// Ideally these would be under `webc.*`
filePath: this.filePath,
slots: {
Expand Down Expand Up @@ -755,7 +755,7 @@ class AstSerializer {
// Used for @html, @raw, @text, @attributes, webc:if, webc:elseif, webc:for
async evaluateAttribute(name, attrContent, options) {
let parentComponent = this.componentManager.get(options.closestParentComponent);
let data = this.dataCascade.getData(options.componentProps, parentComponent?.setupScript, options.injectedData);
let data = this.dataCascade.getData(parentComponent.isTopLevelComponent, options.componentProps, parentComponent?.setupScript, options.injectedData);

let { returns } = await ModuleScript.evaluateScriptInline(attrContent, data, `Check the dynamic attribute: \`${name}="${attrContent}"\`.`, options.closestParentComponent);
return returns;
Expand Down
3 changes: 2 additions & 1 deletion src/componentManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class ComponentManager {
let content = AstQuery.getTextContent(setupScriptNode).toString();

// importantly for caching: this has no attributes or context sensitive things, only global helpers and global data
let data = dataCascade.getData();
let data = dataCascade.getData(true);

// async-friendly
return ModuleScript.evaluateScriptAndReturnAllGlobals(content, filePath, data);
Expand Down Expand Up @@ -225,6 +225,7 @@ class ComponentManager {
},

mode,
isTopLevelComponent,
hasDeclarativeShadowDom,
ignoreRootTag,
scopedStyleHash,
Expand Down
32 changes: 27 additions & 5 deletions src/dataCascade.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
class DataCascade {
constructor() {
this.helpers = {};
this.scopedHelpers = {};
}

setGlobalData(data) {
this.globalData = data;
}

setHelper(name, callback) {
this.helpers[name] = callback;
setHelper(name, callback, isScoped = false) {
if(isScoped) {
this.scopedHelpers[name] = callback;
} else {
this.helpers[name] = callback;
}
}

// the renderAttributes function is one of these
Expand All @@ -17,18 +22,35 @@ class DataCascade {
}

getHelpers() {
// unscoped
return this.helpers;
}

getData(attributes, ...additionalObjects) {
// TODO improve perf by re-using a merged object of the global stuff
/*
* When `isTopLevelComponent` is not true (for inner components, not page-level) this scopes:
* - global data under $data
* - helpers under webc.*
*
* This prevents global data leaking into inner components.
* Notably webc:setup always operates in top level component mode.
*/
getData(isTopLevelComponent, attributes, ...additionalObjects) {
let self = this;
let objs = additionalObjects.reverse();
return Object.assign({}, this.globalData, this.helpers, ...objs, attributes, {
let globals = isTopLevelComponent ? this.globalData : undefined;

let ret = Object.assign({}, globals, this.helpers, ...objs, attributes, {
get $data() {
return self.globalData;
},
webc: {
helpers: this.scopedHelpers,
attributes,
...this.webcGlobals,
}
});

return ret;
}
}

Expand Down
8 changes: 7 additions & 1 deletion test/parserTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -1798,6 +1798,7 @@ test("Using props without “this”", async t => {

let { html, css, js, components } = await component.compile({
data: {
class: "tailwind-lol",
variable: "value",
3: "number key",
"3.1": "lol",
Expand All @@ -1821,13 +1822,18 @@ test("Using props without “this”", async t => {
"./test/stubs/props-no-this.webc",
]);

t.is(html, `<p key="value"></p>
t.is(html, `<p key="tailwind-lol"></p>
<p key="value"></p>
<p key="value"></p>
<p key="value"></p>
<p key="2"></p>
<p key="3"></p>
<p key="number key"></p>
<p key="number key"></p>
<p key="3.1"></p>
<p key="float key"></p>
<p key="float key"></p>
<p key="Boo!"></p>
<p key="Boo!"></p>
<p key="2"></p>
<p key="-urns!"></p>`);
Expand Down
2 changes: 1 addition & 1 deletion test/stubs/components/script-html.webc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<script @html="this.sampleScript"></script>
<script @html="$data.sampleScript"></script>
5 changes: 5 additions & 0 deletions test/stubs/props-no-this.webc
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
<p :key="$data.class"></p>
<p :key="variable"></p>
<p :key="this.variable"></p>
<p :key="$data.variable"></p>
<p :key="2"></p>
<p :key="3"></p>
<p :key="this[3]"></p>
<p :key="$data[3]"></p>
<p :key="3.1"></p>
<p :key="this[3.1]"></p>
<p :key="$data[3.1]"></p>
<p :key="this.nested.object.fn()"></p>
<p :key="$data.nested.object.fn()"></p>
<p :key="false ? 3 : 2"></p>
<p :key="this.nested.object.asyncfn()"></p>
14 changes: 11 additions & 3 deletions webc.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class WebC {

this.customTransforms = {};
this.customHelpers = {};
this.customScopedHelpers = {};
this.globalComponents = {};
this.astOptions = {};
this.bundlerMode = false;
Expand Down Expand Up @@ -130,8 +131,12 @@ class WebC {
this.customTransforms[key] = callback;
}

setHelper(key, callback) {
this.customHelpers[key] = callback;
setHelper(key, callback, isScoped = false) {
if(isScoped) {
this.customScopedHelpers[key] = callback;
} else {
this.customHelpers[key] = callback;
}
}

setAlias(key, folder) {
Expand Down Expand Up @@ -236,7 +241,10 @@ class WebC {
}

for(let name in this.customHelpers) {
ast.setHelper(name, this.customHelpers[name]);
ast.setHelper(name, this.customHelpers[name], false);
}
for(let name in this.customScopedHelpers) {
ast.setHelper(name, this.customScopedHelpers[name], true);
}

await ast.setComponentsByFilePath(this.globalComponents);
Expand Down

0 comments on commit 4294309

Please sign in to comment.