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

🚨 [experiment] check signals v1 #114

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
7 changes: 4 additions & 3 deletions .github/workflows/perf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ on:
push:
branches:
- master
- check-signals
pull_request:
branches: [master]
branches: [master, check-signals]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
Expand All @@ -15,8 +16,8 @@ concurrency:
env:
EXPERIMENT_BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
CONTROL_BRANCH_NAME: "master"
FIDELITY: 100
THROTTLE: 4
FIDELITY: 50
THROTTLE: 1
FORK_NAME: ${{ github.event.pull_request.head.repo.full_name }}

jobs:
Expand Down
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@
"@types/qunit": "^2.19.9",
"autoprefixer": "^10.4.16",
"backburner.js": "^2.8.0",
"express": "^4.18.2",
"glint-environment-gxt": "file:./glint-environment-gxt",
"happy-dom": "^13.0.6",
"nyc": "^15.1.0",
"postcss": "^8.4.33",
"prettier": "^3.1.1",
Expand All @@ -93,16 +96,14 @@
"vite-plugin-circular-dependency": "^0.2.1",
"vite-plugin-dts": "^3.7.0",
"vitest": "^1.1.1",
"zx": "^7.2.3",
"express": "^4.18.2",
"happy-dom": "^13.0.6",
"glint-environment-gxt": "file:./glint-environment-gxt"
"zx": "^7.2.3"
},
"dependencies": {
"@babel/core": "^7.23.6",
"decorator-transforms": "1.1.0",
"@babel/preset-typescript": "^7.23.3",
"@glimmer/syntax": "^0.87.1",
"content-tag": "^1.2.2"
"content-tag": "^1.2.2",
"decorator-transforms": "1.1.0",
"signal-polyfill": "0.1.2"
}
}
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

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

2 changes: 1 addition & 1 deletion src/utils/benchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Application } from '@/components/Application.gts';
import { withRehydration } from '@/utils/rehydration';
import { getDocument } from '@/utils/dom-api';
import { measureRender } from '@/utils/measure-render';
import { setResolveRender } from '@/utils/runtime';
import { setResolveRender } from '@/utils/signals';

export function createBenchmark() {
return {
Expand Down
4 changes: 2 additions & 2 deletions src/utils/dom-api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getNodeCounter, incrementNodeCounter } from '@/utils/dom';
import { IN_SSR_ENV } from './shared';

let $doc =
var $doc =
typeof document !== 'undefined'
? document
: (undefined as unknown as Document);
Expand All @@ -11,7 +11,7 @@ export function setDocument(newDocument: Document) {
export function getDocument() {
return $doc;
}
export const api = {
export var api = {
attr(element: HTMLElement, name: string, value: string | null) {
element.setAttribute(name, value === null ? '' : value);
},
Expand Down
1 change: 1 addition & 0 deletions src/utils/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ export function $_inElement(
} else if (isTagLike(elementRef)) {
appendRef = elementRef.value;
} else {
// @ts-expect-error
appendRef = elementRef;
}
const destructors: Destructors = [];
Expand Down
1 change: 1 addition & 0 deletions src/utils/glimmer-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export function trackedData<T extends object, K extends keyof T>(
let hasInitializer = typeof initializer === 'function';

function getter(self: T) {
// @ts-expect-error
consumeTag(cellFor(self, key));

let value;
Expand Down
1 change: 1 addition & 0 deletions src/utils/if.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export function ifCondition(
}
},
runExistingDestructors,
// @ts-expect-error
opcodeFor(cell, (value) => {
if (throwedError) {
Promise.resolve().then(() => {
Expand Down
3 changes: 3 additions & 0 deletions src/utils/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ export class BasicListComponent<T extends { id: number }> {
if (!isTagLike(tag)) {
if (isArray(tag)) {
console.warn('iterator for @each should be a cell');
// @ts-expect-error
tag = new Cell(tag, 'list tag');
} else if (isFn(originalTag)) {
tag = formula(() => deepFnValue(originalTag), 'list tag');
Expand Down Expand Up @@ -297,6 +298,7 @@ export class SyncListComponent<
constructor(params: ListComponentArgs<T>, outlet: RenderTarget) {
super(params, outlet);
associateDestroyable(params.ctx, [
// @ts-expect-error
opcodeFor(this.tag, (value) => {
this.syncList(value as T[]);
}),
Expand Down Expand Up @@ -331,6 +333,7 @@ export class AsyncListComponent<
constructor(params: ListComponentArgs<any>, outlet: RenderTarget) {
super(params, outlet);
associateDestroyable(params.ctx, [
// @ts-expect-error
opcodeFor(this.tag, async (value) => {
await this.syncList(value as T[]);
}),
Expand Down
115 changes: 13 additions & 102 deletions src/utils/reactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
It's related to Glimmer-VM's `@tracked` system, but without invalidation step.
We explicitly update DOM only when it's needed and only if tags are changed.
*/
import { scheduleRevalidate } from '@/utils/runtime';
import { isFn, isTag, isTagLike, debugContext } from '@/utils/shared';
import { Signal } from "signal-polyfill";


export const asyncOpcodes = new WeakSet<tagOp>();
// List of DOM operations for each tag
Expand Down Expand Up @@ -101,38 +102,31 @@ export function setIsRendering(value: boolean) {
_isRendering = value;
}

function tracker() {
return new Set<Cell>();
}
// "data" cell, it's value can be updated, and it's used to create derived cells
export class Cell<T extends unknown = unknown> {
_value!: T;
_value!: Signal.State<T>;
declare toHTML: () => string;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

YAAAS

[Symbol.toPrimitive]() {
return this.value;
}
_debugName?: string | undefined;
[isTag] = true;
constructor(value: T, debugName?: string) {
this._value = value;
this._value = new Signal.State(value);
if (IS_DEV_MODE) {
this._debugName = debugContext(debugName);
// @ts-expect-error
DEBUG_CELLS.add(this);
}
}
get value() {
if (currentTracker !== null) {
currentTracker.add(this);
}
return this._value;
return this._value.get();
}
set value(value: T) {
this.update(value);
}
update(value: T) {
this._value = value;
tagsToRevalidate.add(this);
scheduleRevalidate();
this._value.set(value);
}
}

Expand Down Expand Up @@ -162,16 +156,9 @@ export function relatedTagsForCell(cell: Cell) {
return relatedTags.get(cell)!;
}

function bindAllCellsToTag(cells: Set<Cell>, tag: MergedCell) {
cells.forEach((cell) => {
const tags = relatedTagsForCell(cell);
tags.add(tag);
});
}

// "derived" cell, it's value is calculated from other cells, and it's value can't be updated
export class MergedCell {
fn: Fn | Function;
fn: Fn | Function;
declare toHTML: () => string;
isConst: boolean = false;
isDestroyed = false;
Expand All @@ -190,95 +177,18 @@ export class MergedCell {
}
destroy() {
this.isDestroyed = true;
opsForTag.delete(this);
if (this.relatedCells !== null) {
this.relatedCells.forEach((cell) => {
const related = relatedTags.get(cell);
if (related !== undefined) {
related.delete(this);
if (related.size === 0) {
relatedTags.delete(cell);
}
}
});
this.relatedCells.clear();
}
if (IS_DEV_MODE) {
DEBUG_MERGED_CELLS.delete(this);
}
}
get value() {
if (this.isDestroyed) {
return;
}

if (this.isConst || !_isRendering || currentTracker !== null) {
return this.fn();
}

try {
currentTracker = tracker();
return this.fn();
} finally {
bindAllCellsToTag(currentTracker!, this);
this.isConst = currentTracker!.size === 0;
this.relatedCells = currentTracker;
currentTracker = null;
}
return this.fn();
}
}

// this function is called when we need to update DOM, values represented by tags are changed
export type tagOp = (...values: unknown[]) => Promise<void> | void;

// this is runtime function, it's called when we need to update DOM for a specific tag
export async function executeTag(tag: Cell | MergedCell) {
let opcode: null | tagOp = null;
// we always have ops for a tag
if (!opsForTag.has(tag)) {
return;
}
const ops = opsFor(tag)!;
if (TRY_CATCH_ERROR_HANDLING) {
try {
const value = tag.value;
for (const op of ops) {
opcode = op;
if (asyncOpcodes.has(op)) {
await op(value);
} else {
op(value);
}
}
} catch (e: any) {
if (IS_DEV_MODE) {
console.error({
message: 'Error executing tag',
error: e,
tag,
opcode: opcode?.toString(),
});
}
if (opcode) {
let index = ops.indexOf(opcode);
if (index > -1) {
ops.splice(index, 1);
}
}
}
} else {
const value = tag.value;
for (const op of ops) {
opcode = op;
if (asyncOpcodes.has(op)) {
await op(value);
} else {
op(value);
}
}
}
}

// this is function to create a reactive cell from an object property
export function cellFor<T extends object, K extends keyof T>(
obj: T,
Expand All @@ -292,6 +202,7 @@ export function cellFor<T extends object, K extends keyof T>(
obj[key],
`${obj.constructor.name}.${String(key)}`,
);
// @ts-expect-error
refs.set(key, cellValue);
cellsMap.set(obj, refs);
Object.defineProperty(obj, key, {
Expand Down Expand Up @@ -327,10 +238,10 @@ export function cell<T>(value: T, debugName?: string) {
}

export function inNewTrackingFrame(callback: () => void) {
const existingTracker = currentTracker;
currentTracker = null;
// const existingTracker = currentTracker;
// currentTracker = null;
callback();
currentTracker = existingTracker;
// currentTracker = existingTracker;
}

export function getTracker() {
Expand Down
4 changes: 3 additions & 1 deletion src/utils/rehydration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { type ComponentReturnType } from '@/utils/component';
import { getNodeCounter, resetNodeCounter } from '@/utils/dom';
import { api as rehydrationDomApi } from '@/utils/rehydration-dom-api';
import { api as domApi } from '@/utils/dom-api';

var originalDomAPI = { ...domApi };
const withRehydrationStack: HTMLElement[] = [];
const commentsToRehydrate: Comment[] = [];
let rehydrationScheduled = false;
Expand Down Expand Up @@ -109,8 +111,8 @@ function pushToStack(node: HTMLElement, isFirst = false) {
}
}

const originalDomAPI = { ...domApi };
function patchDOMAPI() {
originalDomAPI = { ...domApi };
domApi.attr = rehydrationDomApi.attr;
domApi.comment = rehydrationDomApi.comment;
// @ts-expect-error
Expand Down
Loading
Loading