Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle slot updates where parent component has many variables #4078

Merged
merged 5 commits into from
Dec 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
**/_actual.js
**/expected.js
**/_output/**.js
test/*/samples/*/output.js
node_modules

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ node_modules
/test/sourcemaps/samples/*/output.css.map
/yarn-error.log
_actual*.*
_output
/types

/site/cypress/screenshots/
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"acorn": "^7.1.0",
"agadoo": "^1.1.0",
"c8": "^5.0.1",
"code-red": "0.0.26",
"code-red": "0.0.27",
"codecov": "^3.5.0",
"css-tree": "1.0.0-alpha22",
"eslint": "^6.3.0",
Expand Down
57 changes: 19 additions & 38 deletions src/compiler/compile/render_dom/Renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,6 @@ export default class Renderer {
? x`$$self.$$.dirty`
: x`#dirty`) as Identifier | MemberExpression;

let bitmask;
const get_bitmask = () => {
const bitmask: BitMasks = [];
names.forEach((name) => {
Expand All @@ -228,48 +227,30 @@ export default class Renderer {
return bitmask;
};

let operator;
let left;
let right;

return {
get type() {
// we make the type a getter, even though it's always
// a BinaryExpression, because it gives us an opportunity
// to lazily create the node. TODO would be better if
// context was determined before rendering, so that
// this indirection was unnecessary
if (!bitmask) {
bitmask = get_bitmask();

if (!bitmask.length) {
({ operator, left, right } = x`${dirty} & /*${names.join(', ')}*/ 0` as BinaryExpression);
} else if (renderer.context_overflow) {
const expression = bitmask
.map((b, i) => ({ b, i }))
.filter(({ b }) => b)
.map(({ b, i }) => x`${dirty}[${i}] & /*${b.names.join(', ')}*/ ${b.n}`)
.reduce((lhs, rhs) => x`${lhs} | ${rhs}`);

({ operator, left, right } = expression as BinaryExpression);
} else {
({ operator, left, right } = x`${dirty} & /*${names.join(', ')}*/ ${bitmask[0].n}` as BinaryExpression);
}
// Using a ParenthesizedExpression allows us to create
// the expression lazily. TODO would be better if
// context was determined before rendering, so that
// this indirection was unnecessary
type: 'ParenthesizedExpression',
get expression() {
const bitmask = get_bitmask();

if (!bitmask.length) {
return x`${dirty} & /*${names.join(', ')}*/ 0` as BinaryExpression;
}

if (renderer.context_overflow) {
return bitmask
.map((b, i) => ({ b, i }))
.filter(({ b }) => b)
.map(({ b, i }) => x`${dirty}[${i}] & /*${b.names.join(', ')}*/ ${b.n}`)
.reduce((lhs, rhs) => x`${lhs} | ${rhs}`);
}

return 'BinaryExpression';
},
get operator() {
return operator;
},
get left() {
return left;
},
get right() {
return right;
return x`${dirty} & /*${names.join(', ')}*/ ${bitmask[0].n}` as BinaryExpression;
}
} as Expression;
} as any;
}

reference(node: string | Identifier | MemberExpression) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Let from '../../../nodes/Let';
import { x, p } from 'code-red';
import Block from '../../Block';
import TemplateScope from '../../../nodes/shared/TemplateScope';
import { BinaryExpression } from 'estree';

export function get_slot_definition(block: Block, scope: TemplateScope, lets: Let[]) {
if (lets.length === 0) return { block, scope };
Expand All @@ -28,21 +29,48 @@ export function get_slot_definition(block: Block, scope: TemplateScope, lets: Le
properties: Array.from(names).map(name => p`${block.renderer.context_lookup.get(name).index}: ${name}`)
};

const changes = Array.from(names)
.map(name => {
const { context_lookup } = block.renderer;
const { context_lookup } = block.renderer;

const literal = {
type: 'Literal',
get value() {
// i am well aware that this code is gross
// TODO make it less gross
const changes = {
type: 'ParenthesizedExpression',
get expression() {
if (block.renderer.context_overflow) {
const grouped = [];

Array.from(names).forEach(name => {
const i = context_lookup.get(name).index.value as number;
return 1 << i;
const g = Math.floor(i / 31);

if (!grouped[g]) grouped[g] = [];
grouped[g].push({ name, n: i % 31 });
});

const elements = [];

for (let g = 0; g < grouped.length; g += 1) {
elements[g] = grouped[g]
? grouped[g]
.map(({ name, n }) => x`${name} ? ${1 << n} : 0`)
.reduce((lhs, rhs) => x`${lhs} | ${rhs}`)
: x`0`;
}
};

return x`${name} ? ${literal} : 0`;
})
.reduce((lhs, rhs) => x`${lhs} | ${rhs}`);
return {
type: 'ArrayExpression',
elements
};
}

return Array.from(names)
.map(name => {
const i = context_lookup.get(name).index.value as number;
return x`${name} ? ${1 << i} : 0`;
})
.reduce((lhs, rhs) => x`${lhs} | ${rhs}`) as BinaryExpression;
}
};

return {
block,
Expand Down
20 changes: 17 additions & 3 deletions src/runtime/internal/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,23 @@ export function get_slot_context(definition, ctx, $$scope, fn) {
}

export function get_slot_changes(definition, $$scope, dirty, fn) {
return definition[2] && fn
? $$scope.dirty | definition[2](fn(dirty))
: $$scope.dirty;
if (definition[2] && fn) {
const lets = definition[2](fn(dirty));

if (typeof $$scope.dirty === 'object') {
const merged = [];
const len = Math.max($$scope.dirty.length, lets.length);
for (let i = 0; i < len; i += 1) {
merged[i] = $$scope.dirty[i] | lets[i];
}

return merged;
}

return $$scope.dirty | lets;
}

return $$scope.dirty;
}

export function exclude_internal_props(props) {
Expand Down
14 changes: 14 additions & 0 deletions test/helpers.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as jsdom from 'jsdom';
import * as assert from 'assert';
import * as glob from 'tiny-glob/sync.js';
import * as path from 'path';
import * as fs from 'fs';
import * as colors from 'kleur';

Expand Down Expand Up @@ -237,3 +238,16 @@ export function useFakeTimers() {
}
};
}

export function mkdirp(dir) {
const parent = path.dirname(dir);
if (parent === dir) return;

mkdirp(parent);

try {
fs.mkdirSync(dir);
} catch (err) {
// do nothing
}
}
33 changes: 31 additions & 2 deletions test/runtime/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import * as path from "path";
import * as fs from "fs";
import { rollup } from 'rollup';
import * as virtual from 'rollup-plugin-virtual';
import * as glob from 'tiny-glob/sync.js';
import { clear_loops, flush, set_now, set_raf } from "../../internal";

import {
showOutput,
loadConfig,
loadSvelte,
env,
setupHtmlEqual
setupHtmlEqual,
mkdirp
} from "../helpers.js";

let svelte$;
Expand Down Expand Up @@ -90,6 +92,33 @@ describe("runtime", () => {

const window = env();

glob('**/*.svelte', { cwd }).forEach(file => {
if (file[0] === '_') return;

const dir = `${cwd}/_output/${hydrate ? 'hydratable' : 'normal'}`;
const out = `${dir}/${file.replace(/\.svelte$/, '.js')}`;

if (fs.existsSync(out)) {
fs.unlinkSync(out);
}

mkdirp(dir);

try {
const { js } = compile(
fs.readFileSync(`${cwd}/${file}`, 'utf-8'),
{
...compileOptions,
filename: file
}
);

fs.writeFileSync(out, js.code);
} catch (err) {
// do nothing
}
});

return Promise.resolve()
.then(() => {
// hack to support transition tests
Expand Down Expand Up @@ -195,7 +224,7 @@ describe("runtime", () => {
} else {
throw err;
}
}).catch(err => {
}).catch(err => {
failed.add(dir);
showOutput(cwd, compileOptions, compile); // eslint-disable-line no-console
throw err;
Expand Down
5 changes: 5 additions & 0 deletions test/runtime/samples/bitmask-overflow-slot/Echo.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script>
export let dummy;
</script>

<slot dummy={dummy}></slot>
Loading