UNPKG

lively.classes

Version:
185 lines (157 loc) 8.42 kB
/*global beforeEach, afterEach, describe, it*/ import { expect } from "mocha-es6"; import { string } from "lively.lang"; import { classToFunctionTransform } from "../class-to-function-transform.js"; import { member } from "lively.ast/lib/nodes.js"; import { parse } from "lively.ast/lib/parser.js"; import stringify from "lively.ast/lib/stringify.js"; function classTemplate(className, superClassName, methodString, classMethodString, classHolder, moduleMeta, useClassHolder = true, start, end) { if (methodString.includes("\n")) methodString = string.indent(methodString, " ", 2).replace(/^\s+/, ""); if (classMethodString.includes("\n")) classMethodString = string.indent(classMethodString, " ", 2).replace(/^\s+/, ""); if (!className) useClassHolder = false; var classFunctionHeader = className ? `function ${className}` : "function "; if (useClassHolder) classFunctionHeader = `__lively_classholder__.hasOwnProperty("${className}") && typeof __lively_classholder__.${className} === "function" ? __lively_classholder__.${className} : __lively_classholder__.${className} = ${classFunctionHeader}` return `function (superclass) { var __lively_classholder__ = ${classHolder}; var __lively_class__ = ${classFunctionHeader}(__first_arg__) { if (__first_arg__ && __first_arg__[Symbol.for("lively-instance-restorer")]) { } else { this[Symbol.for("lively-instance-initialize")].apply(this, arguments); } }; return initializeClass(__lively_class__, superclass, ${methodString}, ${classMethodString}, __lively_classholder__, ${moduleMeta}, { start: ${start}, end: ${end} }); }(${superClassName});` } function classTemplateDecl(className, superClassName, methodString, classMethodString, classHolder, moduleMeta, start, end) { return `var ${className} = ${classTemplate(className, superClassName, methodString, classMethodString, classHolder, moduleMeta, true, start, end)}` } var opts = { classHolder: {type: "Identifier", name: "_rec"}, functionNode: {type: "Identifier", name: "initializeClass"}, addDeclarations: false }; describe("class transform", () => { it("is translated into class initializer function", () => expect(stringify(classToFunctionTransform("class Foo {}", opts))).to.equal( classTemplateDecl('Foo', 'undefined', 'undefined', 'undefined', "_rec", 'undefined', 0, 12))); it("with class expressions", () => expect(stringify(classToFunctionTransform("var x = class Foo {}", opts))).to.equal( `var x = ${classTemplate('Foo', 'undefined', 'undefined', 'undefined', '_rec', 'undefined', false, 8, 20)}`)); it("with anonymous class expressions", () => expect(stringify(classToFunctionTransform("var x = class {}", opts))).to.equal( `var x = ${classTemplate(undefined, 'undefined', 'undefined', 'undefined', '_rec', 'undefined', undefined, 8, 16)}`)); it("with methods", () => expect(stringify(classToFunctionTransform("class Foo {m() { return 23; }}", opts))).to.equal( classTemplateDecl("Foo", undefined, `[{ key: "m", value: function Foo_m_() { return 23; } }]`, "undefined", "_rec", "undefined", 0, 30))); it("with class side methods", () => expect(stringify(classToFunctionTransform("class Foo {static m() { return 23; }}", opts))).to.equal( classTemplateDecl("Foo", undefined, "undefined", `[{ key: "m", value: function Foo_m_() { return 23; } }]`, "_rec", "undefined", 0, 37))); it("with class side methods inheritance + super call", () => expect(stringify(classToFunctionTransform("class Foo2 extends Foo {static m() { return super.m() + 1; }}", opts))).to.equal( classTemplateDecl("Foo2", "Foo", "undefined", `[{ key: "m", value: function Foo2_m_() { return initializeClass._get(Object.getPrototypeOf(__lively_class__), "m", this).call(this) + 1; } }]`, "_rec", "undefined", 0, 61))); it("with superclass", () => expect(stringify(classToFunctionTransform("class Foo extends Bar {}", opts))).to.equal( classTemplateDecl('Foo', 'Bar', 'undefined', 'undefined', "_rec", 'undefined', 0, 24))); it("with supercall", () => expect(stringify(classToFunctionTransform("class Foo extends Bar {m() { super.m(a, b, c); }}", opts))).to.equal( classTemplateDecl("Foo", "Bar", `[{ key: "m", value: function Foo_m_() { initializeClass._get(Object.getPrototypeOf(__lively_class__.prototype), "m", this).call(this, a, b, c); } }]`, "undefined", "_rec", "undefined",0, 49) )); it("with supercall of computed prop", () => expect(stringify(classToFunctionTransform('class Foo extends Bar {m() { super["f-o-o"](a, b, c); }}', opts))).to.equal( classTemplateDecl("Foo", "Bar", `[{ key: "m", value: function Foo_m_() { initializeClass._get(Object.getPrototypeOf(__lively_class__.prototype), "f-o-o", this).call(this, a, b, c); } }]`, "undefined", "_rec", "undefined", 0, 56) )); it("super getter", () => expect(stringify(classToFunctionTransform("class Foo extends Bar { get x() { return super.x; }}", opts))).to.equal( classTemplateDecl("Foo", "Bar", `[{ key: "x", get: function get() { return initializeClass._get(Object.getPrototypeOf(__lively_class__.prototype), "x", this); } }]`, "undefined", "_rec", "undefined", 0, 52) )); it("with supercall and arguments usage", () => expect(stringify(classToFunctionTransform("class Foo extends Bar {m() { super.m(a, arguments[0], c); }}", opts))).to.equal( classTemplateDecl("Foo", "Bar", `[{ key: "m", value: function Foo_m_() { initializeClass._get(Object.getPrototypeOf(__lively_class__.prototype), "m", this).call(this, a, arguments[0], c); } }]`, "undefined", "_rec", "undefined", 0, 60))); it("constructor is converted to initialize", () => expect(stringify(classToFunctionTransform("class Foo {constructor(arg) { this.x = arg; }}", opts))).to.equal( classTemplateDecl("Foo", "undefined", `[{ key: Symbol.for("lively-instance-initialize"), value: function Foo_initialize_(arg) { this.x = arg; } }]`, "undefined", "_rec", "undefined", 0, 46))); it("super call in constructor is converted to initialize call", () => expect(stringify(classToFunctionTransform("class Foo {constructor(arg) { super(arg, 23); }}", opts))).to.equal( classTemplateDecl("Foo", "undefined", `[{ key: Symbol.for("lively-instance-initialize"), value: function Foo_initialize_(arg) { initializeClass._get(Object.getPrototypeOf(__lively_class__.prototype), Symbol.for("lively-instance-initialize"), this).call(this, arg, 23); } }]`, "undefined", "_rec", "undefined", 0, 48))); it("with export default", () => expect(stringify(classToFunctionTransform("export default class Foo {}", opts))).to.equal( `var Foo = ${classTemplate('Foo', 'undefined', 'undefined', 'undefined', "_rec", 'undefined', true, 15, 27)}\nexport default Foo;`)); it("with export class", () => expect(stringify(classToFunctionTransform("export class Foo {}", opts))).to.equal( `export var Foo = ${classTemplate('Foo', 'undefined', 'undefined', 'undefined', "_rec", 'undefined', true, 7, 19)}`)); it("adds current module accessor", () => expect( stringify(classToFunctionTransform("class Foo {}", Object.assign({}, opts, {currentModuleAccessor: member("foo", "bar")})))) .to.equal( `var Foo = ${classTemplate('Foo', 'undefined', 'undefined', 'undefined', "_rec", 'foo.bar', true, 0, 12)}`)); it("add superclass ref when module accessor available and superclass in toplevel scope", () => { expect( stringify(classToFunctionTransform("var Bar; class Foo extends Bar {}", Object.assign({}, opts, {currentModuleAccessor: member("foo", "bar")})))) .to.equal(`var Bar; ${classTemplateDecl('Foo', `{ referencedAs: "Bar", value: Bar }`, 'undefined', 'undefined', "_rec", 'foo.bar', 9, 33)}`) }); it("doesnt add superclass ref when not in toplevel scope", () => { expect( stringify( classToFunctionTransform( "function zork() { var Bar; class Foo extends Bar {} };", Object.assign({}, opts, {currentModuleAccessor: member("foo", "bar")})))) .to.equal(`function zork() { var Bar; ${string.indent(classTemplateDecl('Foo', 'Bar', 'undefined', 'undefined', "{}", 'foo.bar', 27, 51), " ", 1)} } ;`) }); });