Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
379ca64
TC39 core-infra: TC39 decorator signatures and compiler flip
RaananW Jul 1, 2026
80ef7f0
TC39 core-materials: accessor keyword on all core decorator sites
RaananW Jul 1, 2026
35c1d7c
TC39 materials-lib: accessor keyword on materials library decorator s…
RaananW Jul 1, 2026
2f29f1f
TC39 gui: accessor keyword on fluentMaterial decorator sites
RaananW Jul 1, 2026
02ed4a4
TC39 serializers-3mf: convert 3MF XML decorators to TC39 metadata
RaananW Jul 1, 2026
3ca425d
TC39 test-infra: vitest esbuild class-field semantics + Symbol.metada…
RaananW Jul 1, 2026
f3cce2f
TC39 node-editor-tooling: Symbol.metadata property store for editable…
RaananW Jul 1, 2026
15837ed
TC39 lit-viewer: accessor keyword on viewer Lit element decorators
RaananW Jul 1, 2026
692eb09
TC39 playground-tooling: drop experimentalDecorators from Monaco/snip…
RaananW Jul 1, 2026
a28f28c
TC39 build-tools: use installed tslib for UMD helpers + Dirent fix
RaananW Jul 1, 2026
67febed
TC39 node-editor-tooling: defer WebCamInputBlock self-reference in de…
RaananW Jul 1, 2026
2e7536f
TC39 tests: port decorator + accessor behavior coverage
RaananW Jul 1, 2026
d8d307f
TC39 decorators: run Symbol.metadata polyfill before decorated classe…
RaananW Jul 2, 2026
1b73d6c
TC39 class-fields: declare (not override) the two fields clobbered by…
RaananW Jul 2, 2026
1637c9d
TC39 Native: emit UMD at es2015 and downlevel to ES5 via tsc (not Babel)
RaananW Jul 2, 2026
37e1149
TC39 review: drop unnecessary export of internal __bjsPropStoreKey
RaananW Jul 2, 2026
48a3f0f
Merge remote-tracking branch 'origin/master' into raananw-tc39-pr3-co…
RaananW Jul 3, 2026
bd10f12
TC39 test-infra: lower Stage 3 decorators via esbuild pre-plugin unde…
RaananW Jul 3, 2026
b820cc8
TC39 core: dedupe nativeOverride, add nativeOverride unit tests
RaananW Jul 3, 2026
4547aee
TC39 core: fix TS2684 in nativeOverride.filter native call
RaananW Jul 3, 2026
2fbc6b9
TC39 viewer: lower Stage 3 decorators in dev server via esbuild (Vite…
RaananW Jul 3, 2026
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
255 changes: 4 additions & 251 deletions packages/dev/buildTools/src/pathTransform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,256 +225,9 @@ function TransformerFactory<T extends TransformerNode>(context: ts.Transformatio
export const storeTsLib = () => {
const tsLibPath = path.resolve(path.resolve(".", "tslib.es6.js"));
if (!fs.existsSync(tsLibPath)) {
fs.writeFileSync(tsLibPath, TslibContent);
Comment thread
sebavan marked this conversation as resolved.
// Read from the installed tslib package instead of using a hardcoded copy,
// so that the helpers stay in sync with the TypeScript version.
const tslibSource = require.resolve("tslib/tslib.es6.mjs");
fs.copyFileSync(tslibSource, tsLibPath);
}
};

// tslib 2.4.0
const TslibContent = `
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise */

var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};

export function __extends(d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}

export var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
}
return __assign.apply(this, arguments);
}

export function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
}

export function __decorate(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}

export function __param(paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
}

export function __metadata(metadataKey, metadataValue) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
}

export function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}

export function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}

export var __createBinding = Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
});

export function __exportStar(m, o) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);
}

export function __values(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
}

export function __read(o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
}

/** @deprecated */
export function __spread() {
for (var ar = [], i = 0; i < arguments.length; i++)
ar = ar.concat(__read(arguments[i]));
return ar;
}

/** @deprecated */
export function __spreadArrays() {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
}

export function __spreadArray(to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
}

export function __await(v) {
return this instanceof __await ? (this.v = v, this) : new __await(v);
}

export function __asyncGenerator(thisArg, _arguments, generator) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var g = generator.apply(thisArg, _arguments || []), i, q = [];
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
function fulfill(value) { resume("next", value); }
function reject(value) { resume("throw", value); }
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
}

export function __asyncDelegator(o) {
var i, p;
return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i;
function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; }
}

export function __asyncValues(o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
}

export function __makeTemplateObject(cooked, raw) {
if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
return cooked;
};

var __setModuleDefault = Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
};

export function __importStar(mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
}

export function __importDefault(mod) {
return (mod && mod.__esModule) ? mod : { default: mod };
}

export function __classPrivateFieldGet(receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
}

export function __classPrivateFieldSet(receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
}

export function __classPrivateFieldIn(state, receiver) {
if (receiver === null || (typeof receiver !== "object" && typeof receiver !== "function")) throw new TypeError("Cannot use 'in' operator on non-object");
return typeof state === "function" ? receiver === state : state.has(receiver);
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@ export class BakedVertexAnimationManager implements IBakedVertexAnimationManager
*/
@serializeAsTexture()
@expandToProperty("_markSubMeshesAsAttributesDirty")
public texture: Nullable<BaseTexture>;
public accessor texture: Nullable<BaseTexture>;

private _isEnabled = true;
/**
* Enable or disable the vertex animation manager
*/
@serialize()
@expandToProperty("_markSubMeshesAsAttributesDirty")
public isEnabled = true;
public accessor isEnabled = true;

/**
* The animation parameters for the mesh. See setAnimationParameters()
Expand Down
2 changes: 1 addition & 1 deletion packages/dev/core/src/Cameras/arcRotateCamera.pure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,7 @@ export class ArcRotateCamera extends TargetCamera {
/**
* Defines the input associated to the camera.
*/
public override inputs: ArcRotateCameraInputsManager;
declare public inputs: ArcRotateCameraInputsManager;

/**
* Movement controller that provides framerate-independent physics and the declarative
Expand Down
5 changes: 4 additions & 1 deletion packages/dev/core/src/Cameras/flyCamera.pure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ export class FlyCamera extends TargetCamera {
* inherited {@link TargetCamera.movement} to {@link TargetCameraMovement}; the instance is
* created by the {@link TargetCamera} constructor.
*/
public override movement: TargetCameraMovement;
// `declare`, not `override`: the value is created by the base TargetCamera constructor. Under TC39
// decorators a plain field redeclaration is materialized as a constructor field-init that runs after
// super() and would clobber that value back to undefined; `declare` keeps this a type-only narrowing.
Comment on lines +101 to +103

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe include this comment on the other similar ones? I don't think ppl will understand the syntax.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Only two declare sites exist (freeCamera.pure.ts:64 and flyCamera.pure.ts:101) and both already carry the identical comment block.

declare public movement: TargetCameraMovement;

/**
* Gets the input sensibility for mouse input.
Expand Down
4 changes: 2 additions & 2 deletions packages/dev/core/src/Cameras/followCamera.pure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,12 @@ export class FollowCamera extends TargetCamera {
* Define the target of the camera.
*/
@serializeAsMeshReference("lockedTargetId")
public override lockedTarget: Nullable<AbstractMesh>;
public override lockedTarget: Nullable<AbstractMesh> = null;

/**
* Defines the input associated with the camera.
*/
public override inputs: FollowCameraInputsManager;
declare public inputs: FollowCameraInputsManager;

/**
* Instantiates the follow camera.
Expand Down
5 changes: 4 additions & 1 deletion packages/dev/core/src/Cameras/freeCamera.pure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ export class FreeCamera extends TargetCamera {
* the inherited {@link TargetCamera.movement} to {@link TargetCameraMovement}; the instance is
* created by the {@link TargetCamera} constructor.
*/
public override movement: TargetCameraMovement;
// `declare`, not `override`: the value is created by the base TargetCamera constructor. Under TC39
// decorators a plain field redeclaration is materialized as a constructor field-init that runs after
// super() and would clobber that value back to undefined; `declare` keeps this a type-only narrowing.
declare public movement: TargetCameraMovement;

/**
* Gets the input sensibility for a mouse input. (default is 2000.0)
Expand Down
53 changes: 47 additions & 6 deletions packages/dev/core/src/Decorators/nodeDecorator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type Nullable } from "../types";
import { type Scene } from "../scene";
import { MetadataSymbol } from "../Misc/decorators.functions";

/**
* Enum defining the type of properties that can be edited in the property pages in the node editor
Expand Down Expand Up @@ -107,19 +108,59 @@ export function editableInPropertyPage(
groupName: string = "PROPERTIES",
options?: IEditablePropertyOption
) {
return (target: any, propertyKey: string) => {
let propStore: IPropertyDescriptionForEdition[] = target._propStore;
if (!propStore) {
return (_value: unknown, context: { name: string | symbol; metadata: DecoratorMetadataObject }) => {
const meta = context.metadata as DecoratorMetadataObject | undefined;
if (!meta) {
// `context.metadata` is `void 0` when `Symbol.metadata` was not installed before this class
// was evaluated. Importing `MetadataSymbol` from decorators.functions runs the polyfill at
// module load (before this class body), so this should never happen; referencing it here also
// keeps that module-load polyfill anchored against bundler tree-shaking.
throw new Error(
`editableInPropertyPage: decorator metadata is unavailable; the Symbol.metadata (${String(MetadataSymbol)}) polyfill must run before decorated classes are evaluated.`
);
}
let propStore: IPropertyDescriptionForEdition[];
if (Object.prototype.hasOwnProperty.call(meta, __bjsPropStoreKey)) {
propStore = meta[__bjsPropStoreKey] as IPropertyDescriptionForEdition[];
} else {
propStore = [];
target._propStore = propStore;
meta[__bjsPropStoreKey] = propStore;
}
propStore.push({
propertyName: propertyKey,
propertyName: String(context.name),
displayName: displayName,
type: propertyType,
groupName: groupName,
options: options ?? {},
className: target.getClassName(),
className: "",
});
};
}

// eslint-disable-next-line @typescript-eslint/naming-convention
const __bjsPropStoreKey = "__bjs_prop_store__";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would it be cleaner to use a Symbol here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Staing consistent with __bjsSerializableKey whcih is a string as well. But i don't mind doing that if you feel it is needed


/**
* Gets the editable properties for a given target using TC39 decorator metadata.
* Walks the metadata prototype chain to include properties from parent classes.
* @param target - the target object (instance or constructor)
* @returns array of property descriptions
*/
export function GetEditableProperties(target: any): IPropertyDescriptionForEdition[] {
const ctor = typeof target === "function" ? target : target?.constructor;
const metadata: DecoratorMetadataObject | undefined = ctor?.[MetadataSymbol];
if (!metadata) {
return [];
}

const result: IPropertyDescriptionForEdition[] = [];
let currentMeta: any = metadata;
while (currentMeta) {
if (Object.prototype.hasOwnProperty.call(currentMeta, __bjsPropStoreKey)) {
const store = currentMeta[__bjsPropStoreKey] as IPropertyDescriptionForEdition[];
result.push(...store);
}
currentMeta = Object.getPrototypeOf(currentMeta);
}
return result;
}
Loading