chore: format code

This commit is contained in:
Ayo Ayco 2024-12-19 23:11:07 +01:00
parent a362adab90
commit c25c5e03d4
35 changed files with 1365 additions and 615 deletions

28
.vscode/settings.json vendored
View file

@ -1,15 +1,15 @@
{
// "editor.formatOnSave": false,
"js/ts.implicitProjectConfig.checkJs": true,
"cSpell.words": [
"citty",
"consola",
"estree",
"giget",
"mcfly",
"mcflyjs",
"nitropack",
"ultrahtml",
"unstorage"
]
}
// "editor.formatOnSave": false,
"js/ts.implicitProjectConfig.checkJs": true,
"cSpell.words": [
"citty",
"consola",
"estree",
"giget",
"mcfly",
"mcflyjs",
"nitropack",
"ultrahtml",
"unstorage"
]
}

View file

@ -1,5 +1,5 @@
import globals from "globals";
import pluginJs from "@eslint/js";
import globals from 'globals'
import pluginJs from '@eslint/js'
/** @type {import('eslint').Linter.Config[]} */
export default [
@ -7,12 +7,12 @@ export default [
pluginJs.configs.recommended,
{
ignores: [
"dist/*",
".output/*",
".nitro/*",
"node-modules*",
"site/*",
"templates/*",
'dist/*',
'.output/*',
'.nitro/*',
'node-modules*',
'site/*',
'templates/*',
],
},
];
]

View file

@ -1,29 +1,29 @@
#!/usr/bin/env node
import { consola } from "consola";
import { defineCommand } from "citty";
import { execSync } from "node:child_process";
import { consola } from 'consola'
import { defineCommand } from 'citty'
import { execSync } from 'node:child_process'
function build() {
consola.start("Building project...");
try {
execSync(`npx nitropack build`, { stdio: "inherit" });
} catch (err) {
consola.error(err);
}
consola.start('Building project...')
try {
execSync(`npx nitropack build`, { stdio: 'inherit' })
} catch (err) {
consola.error(err)
}
}
export default defineCommand({
meta: {
name: "prepare",
description: "Builds the McFly project for production.",
name: 'prepare',
description: 'Builds the McFly project for production.',
},
async run() {
build()
},
});
})
export const exportedForTest = {
build,
}
}

View file

@ -1,22 +1,22 @@
#!/usr/bin/env node
import { consola } from "consola";
import { defineCommand } from "citty";
import { consola } from 'consola'
import { defineCommand } from 'citty'
function generate() {
consola.box("Generate a McFly building block (In-progress)");
consola.box('Generate a McFly building block (In-progress)')
}
export default defineCommand({
meta: {
name: "prepare",
description: "Generates building blocks for a McFly app.",
name: 'prepare',
description: 'Generates building blocks for a McFly app.',
},
run() {
generate();
generate()
},
});
})
export const exportedForTest = {
generate,
};
}

View file

@ -1,43 +1,43 @@
#!/usr/bin/env node
import { execSync } from "node:child_process";
import { consola } from "consola";
import { defineCommand } from "citty";
import { execSync } from 'node:child_process'
import { consola } from 'consola'
import { defineCommand } from 'citty'
function createNew(args) {
const directory = args.dir || args._dir;
const command = directory
? `npm create mcfly@latest ${directory}`
: "npm create mcfly@latest";
try {
execSync(command, { stdio: "inherit" });
} catch (e) {
consola.error(e);
}
const directory = args.dir || args._dir
const command = directory
? `npm create mcfly@latest ${directory}`
: 'npm create mcfly@latest'
try {
execSync(command, { stdio: 'inherit' })
} catch (e) {
consola.error(e)
}
}
export default defineCommand({
meta: {
name: "prepare",
description: "Creates a new McFly project.",
name: 'prepare',
description: 'Creates a new McFly project.',
},
args: {
dir: {
type: "string",
description: "project root directory",
type: 'string',
description: 'project root directory',
required: false,
},
_dir: {
type: "positional",
description: "project root directory (prefer using `--dir`)",
type: 'positional',
description: 'project root directory (prefer using `--dir`)',
required: false,
},
},
async run({ args }) {
createNew(args)
},
});
})
export const exportedForTest = {
createNew
}
createNew,
}

View file

@ -1,38 +1,38 @@
#!/usr/bin/env node
import { consola } from "consola";
import { defineCommand } from "citty";
import { execSync } from "node:child_process";
import { consola } from 'consola'
import { defineCommand } from 'citty'
import { execSync } from 'node:child_process'
function prepare() {
consola.start("Preparing McFly workspace...");
consola.start('Preparing McFly workspace...')
let err;
let err
try {
execSync("npx nitropack prepare", { stdio: "inherit" });
} catch (e) {
consola.error(e);
err = e;
}
try {
execSync('npx nitropack prepare', { stdio: 'inherit' })
} catch (e) {
consola.error(e)
err = e
}
if (err) {
consola.fail(
"McFly workspace preparation failed. Please make sure dependencies are installed.\n"
);
} else consola.success("Done\n");
if (err) {
consola.fail(
'McFly workspace preparation failed. Please make sure dependencies are installed.\n'
)
} else consola.success('Done\n')
}
export default defineCommand({
meta: {
name: "prepare",
description: "Prepares the McFly workspace.",
name: 'prepare',
description: 'Prepares the McFly workspace.',
},
run() {
prepare();
prepare()
},
});
})
export const exportedForTest = {
prepare
}
prepare,
}

View file

@ -1,46 +1,46 @@
#!/usr/bin/env node
import { consola } from "consola";
import { colorize } from "consola/utils";
import { defineCommand } from "citty";
import { execSync } from "node:child_process";
import { createRequire } from "node:module";
import { consola } from 'consola'
import { colorize } from 'consola/utils'
import { defineCommand } from 'citty'
import { execSync } from 'node:child_process'
import { createRequire } from 'node:module'
async function printInfo() {
try {
const _require = createRequire(import.meta.url);
const mcflyPkg = await _require("@mcflyjs/core/package.json");
const mcflyPkgVersion = `McFly ${colorize("bold", mcflyPkg.version)}`;
const nitroPkg = await _require("nitropack/package.json");
const nitroPkgVersion = `Nitro ${nitroPkg.version}`;
const _require = createRequire(import.meta.url)
const mcflyPkg = await _require('@mcflyjs/core/package.json')
const mcflyPkgVersion = `McFly ${colorize('bold', mcflyPkg.version)}`
const nitroPkg = await _require('nitropack/package.json')
const nitroPkgVersion = `Nitro ${nitroPkg.version}`
consola.log(
`${colorize("blue", mcflyPkgVersion)} ${colorize("dim", nitroPkgVersion)}`
);
`${colorize('blue', mcflyPkgVersion)} ${colorize('dim', nitroPkgVersion)}`
)
} catch (e) {
consola.error(e);
consola.error(e)
}
}
function serve() {
try {
execSync(`npx nitropack dev`, { stdio: "inherit" });
execSync(`npx nitropack dev`, { stdio: 'inherit' })
} catch (e) {
consola.error(e);
consola.error(e)
}
}
export default defineCommand({
meta: {
name: "prepare",
description: "Runs the dev server.",
name: 'prepare',
description: 'Runs the dev server.',
},
async run() {
await printInfo();
serve();
await printInfo()
serve()
},
});
})
export const exportedForTest = {
serve,
printInfo,
};
}

View file

@ -1,23 +1,23 @@
#!/usr/bin/env node
import { defineCommand, runMain } from "citty";
import { defineCommand, runMain } from 'citty'
const main = defineCommand({
meta: {
name: "mcfly",
description: "McFly CLI",
name: 'mcfly',
description: 'McFly CLI',
},
subCommands: {
new: () => import("./commands/new.mjs").then((r) => r.default),
serve: () => import("./commands/serve.mjs").then((r) => r.default),
build: () => import("./commands/build.mjs").then((r) => r.default),
prepare: () => import("./commands/prepare.mjs").then((r) => r.default),
generate: () => import("./commands/generate.mjs").then((r) => r.default),
g: () => import("./commands/generate.mjs").then((r) => r.default),
new: () => import('./commands/new.mjs').then((r) => r.default),
serve: () => import('./commands/serve.mjs').then((r) => r.default),
build: () => import('./commands/build.mjs').then((r) => r.default),
prepare: () => import('./commands/prepare.mjs').then((r) => r.default),
generate: () => import('./commands/generate.mjs').then((r) => r.default),
g: () => import('./commands/generate.mjs').then((r) => r.default),
},
});
})
runMain(main);
runMain(main)
export const exportedForTest = {
main,
};
}

View file

@ -1,46 +1,46 @@
import consola from "consola";
import { vi, expect, test } from "vitest";
import { exportedForTest } from "../commands/build.mjs";
import consola from 'consola'
import { vi, expect, test } from 'vitest'
import { exportedForTest } from '../commands/build.mjs'
const testFn = exportedForTest.build;
const testFn = exportedForTest.build
const mocks = vi.hoisted(() => {
return {
execSync: vi.fn(),
};
});
}
})
vi.mock("node:child_process", () => {
vi.mock('node:child_process', () => {
return {
execSync: mocks.execSync,
};
});
}
})
test("start build with message", () => {
const message = "Building project...";
const spy = vi.spyOn(consola, "start");
test('start build with message', () => {
const message = 'Building project...'
const spy = vi.spyOn(consola, 'start')
testFn();
testFn()
expect(spy).toHaveBeenCalledWith(message);
});
expect(spy).toHaveBeenCalledWith(message)
})
test("execute nitropack build", () => {
const command = "npx nitropack build";
const param = { stdio: "inherit" };
test('execute nitropack build', () => {
const command = 'npx nitropack build'
const param = { stdio: 'inherit' }
testFn();
testFn()
expect(mocks.execSync).toHaveBeenCalledWith(command, param);
});
expect(mocks.execSync).toHaveBeenCalledWith(command, param)
})
test("catch error", () => {
const spy = vi.spyOn(consola, "error");
test('catch error', () => {
const spy = vi.spyOn(consola, 'error')
mocks.execSync.mockImplementationOnce(() => {
throw new Error("hey");
});
throw new Error('hey')
})
testFn();
testFn()
expect(spy).toHaveBeenCalledWith(new Error("hey"));
});
expect(spy).toHaveBeenCalledWith(new Error('hey'))
})

View file

@ -1,15 +1,15 @@
import { expect, test, vi } from "vitest";
import { exportedForTest } from "../commands/generate.mjs";
import consola from "consola";
import { expect, test, vi } from 'vitest'
import { exportedForTest } from '../commands/generate.mjs'
import consola from 'consola'
const testFn = exportedForTest.generate;
const testFn = exportedForTest.generate
test("show box message in-progress", () => {
const spy = vi.spyOn(consola, "box");
test('show box message in-progress', () => {
const spy = vi.spyOn(consola, 'box')
testFn();
const arg = spy.mock.calls[0][0];
testFn()
const arg = spy.mock.calls[0][0]
expect(spy).toHaveBeenCalled();
expect(arg).toContain("In-progress");
});
expect(spy).toHaveBeenCalled()
expect(arg).toContain('In-progress')
})

View file

@ -1,12 +1,12 @@
import { test } from "vitest";
import { exportedForTest } from "..";
import { expect } from "vitest";
import { test } from 'vitest'
import { exportedForTest } from '..'
import { expect } from 'vitest'
const testObj = exportedForTest.main;
const testObj = exportedForTest.main
test("should have correct subcommands", () => {
test('should have correct subcommands', () => {
Object.keys(testObj.subCommands).forEach((key) => {
expect(testObj.subCommands[key]).toBeTypeOf("function");
expect(testObj.subCommands[key].name).toBe(key);
});
});
expect(testObj.subCommands[key]).toBeTypeOf('function')
expect(testObj.subCommands[key].name).toBe(key)
})
})

View file

@ -1,79 +1,79 @@
import { expect, test, vi } from "vitest";
import { exportedForTest } from "../commands/new.mjs";
import { execSync } from "node:child_process";
import consola from "consola";
import { expect, test, vi } from 'vitest'
import { exportedForTest } from '../commands/new.mjs'
import { execSync } from 'node:child_process'
import consola from 'consola'
const testFn = exportedForTest.createNew;
const baseCommand = `npm create mcfly@latest`;
const testFn = exportedForTest.createNew
const baseCommand = `npm create mcfly@latest`
const mocks = vi.hoisted(() => {
return {
execSync: vi.fn(),
};
});
}
})
vi.mock("node:child_process", () => {
vi.mock('node:child_process', () => {
return {
execSync: mocks.execSync,
};
});
}
})
test("execute create mcfly", () => {
const param = { stdio: "inherit" };
test('execute create mcfly', () => {
const param = { stdio: 'inherit' }
testFn({ dir: undefined });
testFn({ dir: undefined })
expect(execSync).toHaveBeenCalledWith(baseCommand, param);
});
expect(execSync).toHaveBeenCalledWith(baseCommand, param)
})
test("execute create mcfly with no dir", () => {
const dir = "fake-dir";
const command = `${baseCommand} ${dir}`;
const param = { stdio: "inherit" };
test('execute create mcfly with no dir', () => {
const dir = 'fake-dir'
const command = `${baseCommand} ${dir}`
const param = { stdio: 'inherit' }
testFn({ dir: undefined });
testFn({ dir: undefined })
expect(execSync).not.toHaveBeenCalledWith(command, param);
});
expect(execSync).not.toHaveBeenCalledWith(command, param)
})
test("execute create mcfly with dir", () => {
const dir = "fake-dir";
const command = `${baseCommand} ${dir}`;
const param = { stdio: "inherit" };
test('execute create mcfly with dir', () => {
const dir = 'fake-dir'
const command = `${baseCommand} ${dir}`
const param = { stdio: 'inherit' }
testFn({ dir });
testFn({ dir })
expect(execSync).toHaveBeenCalledWith(command, param);
});
expect(execSync).toHaveBeenCalledWith(command, param)
})
test("execute create mcfly with _dir", () => {
const dir = "fake-dir";
const command = `${baseCommand} ${dir}`;
const param = { stdio: "inherit" };
test('execute create mcfly with _dir', () => {
const dir = 'fake-dir'
const command = `${baseCommand} ${dir}`
const param = { stdio: 'inherit' }
testFn({ _dir: dir });
testFn({ _dir: dir })
expect(execSync).toHaveBeenCalledWith(command, param);
});
expect(execSync).toHaveBeenCalledWith(command, param)
})
test("execute create mcfly with dir preferred over _dir", () => {
const dir = "preferred-dir";
const command = `${baseCommand} ${dir}`;
const param = { stdio: "inherit" };
test('execute create mcfly with dir preferred over _dir', () => {
const dir = 'preferred-dir'
const command = `${baseCommand} ${dir}`
const param = { stdio: 'inherit' }
testFn({ dir: dir, _dir: "not-preferred" });
testFn({ dir: dir, _dir: 'not-preferred' })
expect(execSync).toHaveBeenCalledWith(command, param);
});
expect(execSync).toHaveBeenCalledWith(command, param)
})
test("catch error", () => {
const dir = "fake-dir";
const spy = vi.spyOn(consola, "error");
test('catch error', () => {
const dir = 'fake-dir'
const spy = vi.spyOn(consola, 'error')
mocks.execSync.mockImplementationOnce(() => {
throw new Error("hey");
});
throw new Error('hey')
})
testFn({ dir });
testFn({ dir })
expect(spy).toHaveBeenCalledWith(new Error("hey"));
});
expect(spy).toHaveBeenCalledWith(new Error('hey'))
})

View file

@ -1,50 +1,50 @@
import { test, expect, vi } from "vitest";
import { exportedForTest } from "../commands/prepare.mjs";
import consola from "consola";
import { execSync } from "node:child_process";
import { test, expect, vi } from 'vitest'
import { exportedForTest } from '../commands/prepare.mjs'
import consola from 'consola'
import { execSync } from 'node:child_process'
const testFn = exportedForTest.prepare;
const testFn = exportedForTest.prepare
const mocks = vi.hoisted(() => {
return {
execSync: vi.fn(),
};
});
}
})
vi.mock("node:child_process", () => {
vi.mock('node:child_process', () => {
return {
execSync: mocks.execSync,
};
});
}
})
test("start prepare script", () => {
const spy = vi.spyOn(consola, "start");
test('start prepare script', () => {
const spy = vi.spyOn(consola, 'start')
testFn();
testFn()
expect(spy).toHaveBeenCalled();
});
expect(spy).toHaveBeenCalled()
})
test("execute nitropack prepare", () => {
const successSpy = vi.spyOn(consola, "success");
const command = "npx nitropack prepare";
const param = { stdio: "inherit" };
test('execute nitropack prepare', () => {
const successSpy = vi.spyOn(consola, 'success')
const command = 'npx nitropack prepare'
const param = { stdio: 'inherit' }
testFn();
testFn()
expect(execSync).toHaveBeenCalledWith(command, param);
expect(successSpy).toHaveBeenCalled();
});
expect(execSync).toHaveBeenCalledWith(command, param)
expect(successSpy).toHaveBeenCalled()
})
test("catch error", () => {
const errSpy = vi.spyOn(consola, "error");
const failSpy = vi.spyOn(consola, "fail");
test('catch error', () => {
const errSpy = vi.spyOn(consola, 'error')
const failSpy = vi.spyOn(consola, 'fail')
mocks.execSync.mockImplementationOnce(() => {
throw new Error();
});
throw new Error()
})
testFn();
testFn()
expect(errSpy).toHaveBeenCalled();
expect(failSpy).toHaveBeenCalled();
});
expect(errSpy).toHaveBeenCalled()
expect(failSpy).toHaveBeenCalled()
})

View file

@ -1,83 +1,83 @@
import { describe, expect, test, vi } from "vitest";
import { exportedForTest } from "../commands/serve.mjs";
import consola from "consola";
import { describe, expect, test, vi } from 'vitest'
import { exportedForTest } from '../commands/serve.mjs'
import consola from 'consola'
describe("FUNCTION: serve()", () => {
const testFn = exportedForTest.serve;
describe('FUNCTION: serve()', () => {
const testFn = exportedForTest.serve
const mocks = vi.hoisted(() => {
return {
execSync: vi.fn(),
};
});
}
})
vi.mock("node:child_process", () => {
vi.mock('node:child_process', () => {
return {
execSync: mocks.execSync,
};
});
}
})
test("execute nitropack serve", async () => {
const command = `npx nitropack dev`;
const param = { stdio: "inherit" };
test('execute nitropack serve', async () => {
const command = `npx nitropack dev`
const param = { stdio: 'inherit' }
testFn();
testFn()
expect(mocks.execSync).toHaveBeenCalledWith(command, param);
});
expect(mocks.execSync).toHaveBeenCalledWith(command, param)
})
test("catch error", () => {
const spy = vi.spyOn(consola, "error");
test('catch error', () => {
const spy = vi.spyOn(consola, 'error')
mocks.execSync.mockImplementationOnce(() => {
throw new Error("hey");
});
throw new Error('hey')
})
testFn();
testFn()
expect(spy).toHaveBeenCalledWith(new Error("hey"));
});
});
expect(spy).toHaveBeenCalledWith(new Error('hey'))
})
})
describe("FUNCTION: printInfo()", () => {
const testFn = exportedForTest.printInfo;
describe('FUNCTION: printInfo()', () => {
const testFn = exportedForTest.printInfo
const createRequireMocks = vi.hoisted(() => {
return {
createRequire: vi.fn(),
};
});
}
})
vi.mock("node:module", () => {
vi.mock('node:module', () => {
return {
createRequire: createRequireMocks.createRequire,
};
});
}
})
test("log mcfly and nitro versions", async () => {
const spy = vi.spyOn(consola, "log");
const fakeMessage = "McFly -1.0.0 Nitro -1.0.0";
test('log mcfly and nitro versions', async () => {
const spy = vi.spyOn(consola, 'log')
const fakeMessage = 'McFly -1.0.0 Nitro -1.0.0'
createRequireMocks.createRequire.mockImplementationOnce(() => {
return () => {
return {
version: "-1.0.0",
};
};
});
version: '-1.0.0',
}
}
})
await testFn();
await testFn()
expect(spy.mock.calls[0][0]).toContain("McFly");
expect(spy.mock.calls[0][0]).toContain("Nitro");
expect(spy).toHaveBeenCalledWith(fakeMessage);
});
expect(spy.mock.calls[0][0]).toContain('McFly')
expect(spy.mock.calls[0][0]).toContain('Nitro')
expect(spy).toHaveBeenCalledWith(fakeMessage)
})
test("catch error", async () => {
test('catch error', async () => {
createRequireMocks.createRequire.mockImplementationOnce(() => {
throw new Error("error");
});
const spy = vi.spyOn(consola, "error");
throw new Error('error')
})
const spy = vi.spyOn(consola, 'error')
await testFn();
await testFn()
expect(spy).toHaveBeenCalledWith(new Error("error"));
});
});
expect(spy).toHaveBeenCalledWith(new Error('error'))
})
})

View file

@ -1,12 +1,12 @@
import { coverageConfigDefaults, defineConfig } from "vitest/config";
import { coverageConfigDefaults, defineConfig } from 'vitest/config'
export default defineConfig({
test: {
coverage: {
enabled: true,
provider: "v8",
reporter: ["html", "text"],
exclude: ["html/**", ...coverageConfigDefaults.exclude],
provider: 'v8',
reporter: ['html', 'text'],
exclude: ['html/**', ...coverageConfigDefaults.exclude],
},
},
});
})

View file

@ -9,33 +9,33 @@
export default function () {
return {
framework: {
name: "McFly",
name: 'McFly',
},
compatibilityDate: "2024-12-08",
compatibilityDate: '2024-12-08',
devServer: {
watch: ["./src/pages", "./src/components"],
watch: ['./src/pages', './src/components'],
},
serverAssets: [
{
baseName: "pages",
dir: "./src/pages",
baseName: 'pages',
dir: './src/pages',
},
{
baseName: "components",
dir: "./src/components",
baseName: 'components',
dir: './src/components',
},
],
imports: {
presets: [
{
from: "web-component-base",
imports: ["WebComponent", "html", "attachEffect"],
from: 'web-component-base',
imports: ['WebComponent', 'html', 'attachEffect'],
},
{
from: "@mcflyjs/core",
imports: ["useMcFlyRoute", "defineMcFlyConfig"],
from: '@mcflyjs/core',
imports: ['useMcFlyRoute', 'defineMcFlyConfig'],
},
],
},
};
}
}

View file

@ -10,5 +10,5 @@
* @returns {function(): McFlyConfig}
*/
export function defineMcFlyConfig(config) {
return () => config;
return () => config
}

View file

@ -1,6 +1,6 @@
import { eventHandler } from "h3";
import { ELEMENT_NODE, parse, render, renderSync, walkSync } from "ultrahtml";
import { parseScript } from "esprima";
import { eventHandler } from 'h3'
import { ELEMENT_NODE, parse, render, renderSync, walkSync } from 'ultrahtml'
import { parseScript } from 'esprima'
/**
* @typedef {import('./define-config.js').McFlyConfig} Config
@ -21,36 +21,36 @@ import { parseScript } from "esprima";
*/
export function useMcFlyRoute({ config, storage }) {
return eventHandler(async (event) => {
const { path } = event;
const { components: componentType } = config();
let html = await getHtml(path, storage);
const { path } = event
const { components: componentType } = config()
let html = await getHtml(path, storage)
if (html) {
const transforms = [doSetUp, deleteServerScripts];
const transforms = [doSetUp, deleteServerScripts]
for (const transform of transforms) {
html = transform(html.toString());
html = transform(html.toString())
}
html = await useFragments(html.toString(), storage);
html = await useFragments(html.toString(), storage)
if (!!componentType && !!html) {
html = await insertRegistry(html.toString(), componentType, storage);
html = await insertRegistry(html.toString(), componentType, storage)
}
}
return (
html ??
new Response(
"😱 ERROR 404: Not found. You can put a 404.html on the ./src/pages directory to customize this error page.",
'😱 ERROR 404: Not found. You can put a 404.html on the ./src/pages directory to customize this error page.',
{ status: 404 }
)
);
});
)
})
}
function getPurePath(path) {
return path.split("?")[0];
return path.split('?')[0]
}
/**
@ -60,17 +60,17 @@ function getPurePath(path) {
* @returns {Promise<StorageValue>}
*/
async function getHtml(path, storage) {
const purePath = getPurePath(path);
const purePath = getPurePath(path)
const rawPath =
purePath[purePath.length - 1] === "/" ? purePath.slice(0, -1) : purePath;
const filename = rawPath === "" ? "/index.html" : `${rawPath}.html`;
const fallback = getPath(rawPath + "/index.html");
const filePath = getPath(filename);
let html = await storage.getItem(filePath);
if (!html) html = await storage.getItem(fallback);
if (!html) html = await storage.getItem(getPath("/404.html"));
purePath[purePath.length - 1] === '/' ? purePath.slice(0, -1) : purePath
const filename = rawPath === '' ? '/index.html' : `${rawPath}.html`
const fallback = getPath(rawPath + '/index.html')
const filePath = getPath(filename)
let html = await storage.getItem(filePath)
if (!html) html = await storage.getItem(fallback)
if (!html) html = await storage.getItem(getPath('/404.html'))
return html;
return html
}
/**
@ -79,7 +79,7 @@ async function getHtml(path, storage) {
* @returns {string}
*/
function getPath(filename) {
return `assets:pages${filename}`;
return `assets:pages${filename}`
}
/**
@ -90,21 +90,21 @@ function getPath(filename) {
* @returns {Promise<string>}
*/
async function insertRegistry(html, type, storage) {
const ast = parse(html);
const componentFiles = await getFiles(type, storage);
const ast = parse(html)
const componentFiles = await getFiles(type, storage)
const availableComponents = componentFiles.map((key) =>
key.replace(`.${type}`, "")
);
key.replace(`.${type}`, '')
)
const usedCustomElements = [];
const usedCustomElements = []
walkSync(ast, (node) => {
const usedElement = availableComponents.find((name) => name === node.name);
const usedElement = availableComponents.find((name) => name === node.name)
if (node.type === ELEMENT_NODE && !!usedElement) {
usedCustomElements.push(usedElement);
usedCustomElements.push(usedElement)
}
});
})
// insert registry script to head
if (usedCustomElements.length > 0) {
@ -112,15 +112,15 @@ async function insertRegistry(html, type, storage) {
usedCustomElements,
type,
storage
);
)
walkSync(ast, (node) => {
if (node.type === ELEMENT_NODE && node.name === "head") {
node.children.push(parse(registryScript));
if (node.type === ELEMENT_NODE && node.name === 'head') {
node.children.push(parse(registryScript))
}
});
})
}
return render(ast);
return render(ast)
}
/**
@ -131,41 +131,41 @@ async function insertRegistry(html, type, storage) {
* @returns {Promise<string>}
*/
async function buildRegistry(usedCustomElements, type, storage) {
let registryScript = `<script type='module'>`;
let isBaseClassImported = false;
let classesImported = [];
let registryScript = `<script type='module'>`
let isBaseClassImported = false
let classesImported = []
for (const name of usedCustomElements) {
const content = await storage.getItem(`assets:components:${name}.${type}`);
if (!content) continue;
const content = await storage.getItem(`assets:components:${name}.${type}`)
if (!content) continue
const evalStore = eval(
`class WebComponent {}; class HTMLElement {}; (${content.toString()})`
);
)
if (isConstructor(evalStore)) {
const className = new evalStore().constructor.name;
const className = new evalStore().constructor.name
if (!classesImported.includes(className)) {
if (
!isBaseClassImported &&
content.toString().includes("extends WebComponent")
content.toString().includes('extends WebComponent')
) {
const baseClassImport = `import { WebComponent, html, attachEffect } from "https://unpkg.com/web-component-base@2.0.6/index.js";`;
registryScript += baseClassImport;
isBaseClassImported = true;
const baseClassImport = `import { WebComponent, html, attachEffect } from "https://unpkg.com/web-component-base@2.0.6/index.js";`
registryScript += baseClassImport
isBaseClassImported = true
}
registryScript += content;
registryScript += content
registryScript += `customElements.define("${name}", ${className});`;
classesImported.push(className);
registryScript += `customElements.define("${name}", ${className});`
classesImported.push(className)
}
}
}
registryScript += "</script>";
registryScript += '</script>'
return registryScript;
return registryScript
}
/**
@ -175,13 +175,13 @@ async function buildRegistry(usedCustomElements, type, storage) {
*/
function isConstructor(f) {
try {
new f();
new f()
// eslint-disable-next-line no-unused-vars
} catch (err) {
// TODO: verify err is the expected error and then
return false;
return false
}
return true;
return true
}
/**
@ -190,56 +190,56 @@ function isConstructor(f) {
* @returns {string}
*/
function doSetUp(html) {
const ast = parse(html);
const serverScripts = [];
const ast = parse(html)
const serverScripts = []
walkSync(ast, (node) => {
const { attributes } = node;
const attributeKeys = Object.keys(attributes ?? {});
const isServerScript = attributeKeys.some((key) => key.includes("server:"));
const { attributes } = node
const attributeKeys = Object.keys(attributes ?? {})
const isServerScript = attributeKeys.some((key) => key.includes('server:'))
if (
node.type === ELEMENT_NODE &&
node.name === "script" &&
node.name === 'script' &&
isServerScript
) {
const scripts = node.children.map((child) => child.value);
const script = cleanScript(scripts);
serverScripts.push(script);
const scripts = node.children.map((child) => child.value)
const script = cleanScript(scripts)
serverScripts.push(script)
}
});
})
const setupMap = {};
const setupMap = {}
serverScripts.forEach((script) => {
const { body } = parseScript(script);
const { body } = parseScript(script)
const keys = body
.filter((n) => n.type === "VariableDeclaration")
.map((n) => n["declarations"][0].id.name);
.filter((n) => n.type === 'VariableDeclaration')
.map((n) => n['declarations'][0].id.name)
const constructor = `(function(){}.constructor)(\`${script}; return {${keys.join(
","
)}}\`);`;
const evalStore = eval(constructor);
Object.assign(setupMap, new evalStore());
});
','
)}}\`);`
const evalStore = eval(constructor)
Object.assign(setupMap, new evalStore())
})
const regex = /{{(.*?)}}/g;
let match;
const regex = /{{(.*?)}}/g
let match
while ((match = regex.exec(html))) {
let [key, value] = match;
value = value.replace(/\s/g, "");
let [key, value] = match
value = value.replace(/\s/g, '')
// nested objects
const keys = value.split(".");
let finalValue = "";
let setupCopy = setupMap;
const keys = value.split('.')
let finalValue = ''
let setupCopy = setupMap
keys.forEach((i) => {
finalValue = setupCopy[i];
setupCopy = finalValue;
});
finalValue = setupCopy[i]
setupCopy = finalValue
})
html = html.replace(key, finalValue);
html = html.replace(key, finalValue)
}
return html;
return html
}
/**
@ -248,17 +248,17 @@ function doSetUp(html) {
* @returns {string}
*/
function deleteServerScripts(html) {
const ast = parse(html);
const ast = parse(html)
walkSync(ast, (node) => {
const { attributes } = node;
const attributeKeys = Object.keys(attributes ?? {});
const isServerScript = attributeKeys.some((key) => key.includes("server:"));
const { attributes } = node
const attributeKeys = Object.keys(attributes ?? {})
const isServerScript = attributeKeys.some((key) => key.includes('server:'))
if (isServerScript && !!node.parent) {
node.parent.children.splice(node.parent.children.indexOf(node), 1);
node.parent.children.splice(node.parent.children.indexOf(node), 1)
}
});
})
return renderSync(ast);
return renderSync(ast)
}
/**
@ -267,11 +267,11 @@ function deleteServerScripts(html) {
* @returns {string}
*/
function cleanScript(scripts) {
let script = scripts.map((s) => s.trim()).join(" ");
let script = scripts.map((s) => s.trim()).join(' ')
script = removeComments(script);
script = removeComments(script)
return script.replace(/\n/g, "").replace(/\s+/g, " ");
return script.replace(/\n/g, '').replace(/\s+/g, ' ')
}
/**
@ -281,11 +281,11 @@ function cleanScript(scripts) {
*/
function isComment(node) {
return (
node.type === "Line" ||
node.type === "Block" ||
node.type === "BlockComment" ||
node.type === "LineComment"
);
node.type === 'Line' ||
node.type === 'Block' ||
node.type === 'BlockComment' ||
node.type === 'LineComment'
)
}
/**
@ -294,25 +294,25 @@ function isComment(node) {
* @returns {string}
*/
function removeComments(script) {
const entries = [];
const entries = []
parseScript(script, { comment: true }, function (node, meta) {
if (isComment(node)) {
entries.push({
start: meta.start.offset,
end: meta.end.offset,
});
})
}
});
})
entries
.sort((a, b) => {
return b.end - a.end;
return b.end - a.end
})
.forEach((n) => {
script = script.slice(0, n.start) + script.slice(n.end);
});
script = script.slice(0, n.start) + script.slice(n.end)
})
return script;
return script
}
/**
@ -322,44 +322,44 @@ function removeComments(script) {
* @returns {Promise<string>}
*/
async function useFragments(html, storage) {
const fragmentFiles = await getFiles("html", storage);
const fragmentFiles = await getFiles('html', storage)
const availableFragments = fragmentFiles.reduce((acc, key) => {
return {
...acc,
[key.replace(".html", "")]: "",
};
}, {});
const ast = parse(html);
[key.replace('.html', '')]: '',
}
}, {})
const ast = parse(html)
for (const key in availableFragments) {
/**
* @type string | null
*/
let text = await storage.getItem("assets:components:" + key + ".html");
if (!text) continue;
availableFragments[key] = text.replace(/\n/g, "").replace(/\s+/g, " ");
let text = await storage.getItem('assets:components:' + key + '.html')
if (!text) continue
availableFragments[key] = text.replace(/\n/g, '').replace(/\s+/g, ' ')
}
walkSync(ast, (node) => {
const selector = Object.keys(availableFragments).find(
(name) => name === node.name
);
)
if (node.type === ELEMENT_NODE && !!selector) {
const index = node.parent.children.indexOf(node);
const index = node.parent.children.indexOf(node)
/**
* @type {HtmlNode}
*/
const fragmentNode = parse(availableFragments[selector]);
const fragmentNode = parse(availableFragments[selector])
replaceSlots(fragmentNode, node);
replaceSlots(fragmentNode, node)
node.parent.children[index] = fragmentNode;
node.parent.children[index] = fragmentNode
}
});
})
return render(ast);
return render(ast)
}
/**
@ -369,32 +369,32 @@ async function useFragments(html, storage) {
* @returns {void}
*/
function replaceSlots(fragmentNode, node) {
let slotted = [];
const containsAll = (arr, target) => target.every((v) => arr.includes(v));
let slotted = []
const containsAll = (arr, target) => target.every((v) => arr.includes(v))
walkSync(fragmentNode, (n) => {
if (n.type === ELEMENT_NODE && n.name === "slot") {
if (n.type === ELEMENT_NODE && n.name === 'slot') {
// find node child with same name attribute
const currentSlotName = n.attributes?.["name"] ?? null;
let nodeChildren = [];
const currentSlotName = n.attributes?.['name'] ?? null
let nodeChildren = []
if (currentSlotName === null) {
nodeChildren = node.children.filter(
(child) => !child.attributes?.["slot"]
);
(child) => !child.attributes?.['slot']
)
} else {
nodeChildren = node.children.filter((child) => {
const childSlotName = child.attributes?.["slot"];
return childSlotName === currentSlotName;
});
const childSlotName = child.attributes?.['slot']
return childSlotName === currentSlotName
})
}
if (nodeChildren.length > 0 && !containsAll(slotted, nodeChildren)) {
slotted = [...slotted, ...nodeChildren];
const index = n.parent.children.indexOf(n);
n.parent.children.splice(index, 1, ...nodeChildren);
slotted = [...slotted, ...nodeChildren]
const index = n.parent.children.indexOf(n)
n.parent.children.splice(index, 1, ...nodeChildren)
}
}
});
})
}
/**
@ -404,7 +404,7 @@ function replaceSlots(fragmentNode, node) {
* @returns {Promise<string[]>}
*/
async function getFiles(type, storage) {
return (await storage.getKeys("assets:components"))
.map((key) => key.replace("assets:components:", ""))
.filter((key) => key.includes(type));
return (await storage.getKeys('assets:components'))
.map((key) => key.replace('assets:components:', ''))
.filter((key) => key.includes(type))
}

View file

@ -1,2 +1,2 @@
export { defineMcFlyConfig } from "./define-config.js";
export { useMcFlyRoute } from "./event-handler.js";
export { defineMcFlyConfig } from './define-config.js'
export { useMcFlyRoute } from './event-handler.js'

View file

@ -1,12 +1,12 @@
#!/usr/bin/env node
import { consola } from "consola";
import { colorize } from "consola/utils";
import { downloadTemplate } from "giget";
import { spawnSync } from "node:child_process";
import path from "node:path";
import { consola } from 'consola'
import { colorize } from 'consola/utils'
import { downloadTemplate } from 'giget'
import { spawnSync } from 'node:child_process'
import path from 'node:path'
const [, , directoryArg] = process.argv;
const [, , directoryArg] = process.argv
/**
* @typedef {{
@ -23,25 +23,25 @@ const [, , directoryArg] = process.argv;
* Create McFly App
*/
async function create() {
const defaultDirectory = "mcfly-app";
consola.box(`Hello! Welcome to ${colorize("bold", "McFly")}!`);
let directory = directoryArg;
const defaultDirectory = 'mcfly-app'
consola.box(`Hello! Welcome to ${colorize('bold', 'McFly')}!`)
let directory = directoryArg
if (!directory) {
directory =
(await consola.prompt("Give your new project a name:", {
(await consola.prompt('Give your new project a name:', {
placeholder: defaultDirectory,
})) ?? defaultDirectory;
})) ?? defaultDirectory
} else {
consola.success(`Using ${directory} as name.`);
consola.success(`Using ${directory} as name.`)
}
if (typeof directory !== "string") {
return;
if (typeof directory !== 'string') {
return
}
directory = getSafeDirectory(directory);
const hasErrors = await downloadTemplateToDirectory(directory);
directory = getSafeDirectory(directory)
const hasErrors = await downloadTemplateToDirectory(directory)
if (!hasErrors) {
/**
@ -50,33 +50,33 @@ async function create() {
const prompts = [
{
prompt: `Would you like to install the dependencies to ${colorize(
"bold",
'bold',
directory
)}?`,
info: "This might take some time depending on your connectivity.",
startMessage: "Installing dependencies using npm...",
info: 'This might take some time depending on your connectivity.',
startMessage: 'Installing dependencies using npm...',
command: `npm`,
subCommand: "install",
subCommand: 'install',
error: `Install dependencies later with ${colorize(
"yellow",
"npm install"
'yellow',
'npm install'
)}`,
},
{
prompt: "Would you like to initialize your git repository?",
startMessage: "Initializing git repository...",
prompt: 'Would you like to initialize your git repository?',
startMessage: 'Initializing git repository...',
command: `git`,
subCommand: "init",
subCommand: 'init',
error: `Initialize git repository later with ${colorize(
"yellow",
"git init"
'yellow',
'git init'
)}`,
},
];
]
const intentions = await askPrompts(prompts, directory);
const intentions = await askPrompts(prompts, directory)
if (!!intentions && intentions.length > 0)
showResults(directory, intentions[0]);
showResults(directory, intentions[0])
}
}
@ -86,10 +86,10 @@ async function create() {
* @returns string | undefined
*/
function getSafeDirectory(directory) {
const { platform } = process;
const locale = path[platform === `win32` ? `win32` : `posix`];
const localePath = directory.split(path.sep).join(locale.sep);
return path.normalize(localePath);
const { platform } = process
const locale = path[platform === `win32` ? `win32` : `posix`]
const localePath = directory.split(path.sep).join(locale.sep)
return path.normalize(localePath)
}
/**
@ -98,22 +98,22 @@ function getSafeDirectory(directory) {
* @returns Promise<boolean> hasErrors
*/
async function downloadTemplateToDirectory(directory) {
let hasErrors = false;
let hasErrors = false
try {
consola.start(
`Copying template to ${colorize("bold", getSafeDirectory(directory))}...`
);
await downloadTemplate("github:ayoayco/mcfly/templates/basic", {
`Copying template to ${colorize('bold', getSafeDirectory(directory))}...`
)
await downloadTemplate('github:ayoayco/mcfly/templates/basic', {
dir: directory,
});
})
} catch (_ㆆ) {
consola.error(_ㆆ.message);
consola.info("Try a different name.\n");
hasErrors = true;
consola.error(_ㆆ.message)
consola.info('Try a different name.\n')
hasErrors = true
}
return hasErrors;
return hasErrors
}
/**
@ -123,37 +123,37 @@ async function downloadTemplateToDirectory(directory) {
* @returns Array<boolean> | undefined
*/
async function askPrompts(prompts, cwd) {
const results = [];
const results = []
for (const p of prompts) {
const userIntends = await consola.prompt(p.prompt, {
type: "confirm",
});
type: 'confirm',
})
if (typeof userIntends !== "boolean") {
return;
if (typeof userIntends !== 'boolean') {
return
}
if (userIntends) {
p.info && consola.info(p.info);
consola.start(p.startMessage);
p.info && consola.info(p.info)
consola.start(p.startMessage)
try {
spawnSync(p.command, [p.subCommand], {
cwd,
shell: true,
timeout: 100_000,
stdio: "inherit",
});
consola.success("Done!");
stdio: 'inherit',
})
consola.success('Done!')
} catch (_ㆆ) {
consola.error(_ㆆ.message);
consola.info(p.error + "\n");
consola.error(_ㆆ.message)
consola.info(p.error + '\n')
}
}
results.push(userIntends);
results.push(userIntends)
}
return results;
return results
}
/**
@ -163,28 +163,28 @@ async function askPrompts(prompts, cwd) {
*/
function showResults(directory, installDeps) {
let nextActions = [
`Go to your project by running ${colorize("yellow", `cd ${directory}`)}`,
];
`Go to your project by running ${colorize('yellow', `cd ${directory}`)}`,
]
if (!installDeps) {
nextActions.push(
`Install the dependencies with ${colorize("yellow", "npm install")}`
);
`Install the dependencies with ${colorize('yellow', 'npm install')}`
)
}
nextActions = nextActions.concat([
`Start the dev server with ${colorize("yellow", "npm start")}`,
`Join us at ${colorize("blue", "https://ayco.io/gh/McFly")}`,
]);
`Start the dev server with ${colorize('yellow', 'npm start')}`,
`Join us at ${colorize('blue', 'https://ayco.io/gh/McFly')}`,
])
const result = `🎉 Your new ${colorize(
"bold",
"McFly"
'bold',
'McFly'
)} app is ready: ${directory}\n\nNext actions: ${nextActions
.map((action, index) => `\n${++index}. ${action}`)
.join("")}`;
.join('')}`
consola.box(result);
consola.box(result)
}
create();
create()

View file

@ -5,10 +5,10 @@
* @type {import("prettier").Config}
*/
const config = {
trailingComma: "es5",
trailingComma: 'es5',
tabWidth: 2,
semi: false,
singleQuote: true,
};
}
export default config;
export default config

View file

@ -1,4 +1,4 @@
import { defineMcFlyConfig } from "#imports";
import { defineMcFlyConfig } from '#imports'
export default defineMcFlyConfig({
components: "js",
});
components: 'js',
})

View file

@ -1,14 +1,14 @@
export default defineNitroConfig({
extends: "@mcflyjs/config",
extends: '@mcflyjs/config',
devServer: {
watch: ["../packages"],
watch: ['../packages'],
},
routeRules: {
"/chat": {
'/chat': {
redirect: {
to: "https://matrix.to/#/#mcfly:matrix.org",
to: 'https://matrix.to/#/#mcfly:matrix.org',
statusCode: 302,
},
},
@ -19,5 +19,5 @@ export default defineNitroConfig({
brotli: true,
},
compatibilityDate: "2024-12-08",
});
compatibilityDate: '2024-12-08',
})

File diff suppressed because one or more lines are too long

View file

@ -5,5 +5,5 @@
* ...reusable code are in ./src/components
* @see https://ayco.io/gh/McFly#special-directories
*/
import config from "../mcfly.config.mjs";
export default useMcFlyRoute({ config, storage: useStorage() });
import config from '../mcfly.config.mjs'
export default useMcFlyRoute({ config, storage: useStorage() })

View file

@ -4,9 +4,9 @@
*/
class ClickableText extends WebComponent {
onInit() {
this.onclick = () => alert("Thank you for clicking the text!");
this.onclick = () => alert('Thank you for clicking the text!')
}
get template() {
return `<span style="cursor:pointer">Click me too!</span>`;
return `<span style="cursor:pointer">Click me too!</span>`
}
}

View file

@ -1,41 +1,41 @@
class CodeBlockComponent extends HTMLElement {
connectedCallback() {
const trimmed = this.innerHTML.trim();
const lang = this.getAttribute("language");
const inline = this.getAttribute("inline") !== null;
const trimmed = this.innerHTML.trim()
const lang = this.getAttribute('language')
const inline = this.getAttribute('inline') !== null
this.innerHTML = `
<pre id="pre"><code id="code">${trimmed}</code></pre>
`;
`
/**
* @type {HTMLPreElement}
*/
const pre = this.querySelector("#pre");
const pre = this.querySelector('#pre')
if (lang) {
pre.className = `language-${lang}`;
pre.className = `language-${lang}`
}
/**
* @type {Partial<CSSStyleDeclaration>}
*/
const style = {
background: "#f5f2f0",
padding: "1em",
margin: "1em 0",
fontSize: "large",
overflow: "auto",
borderRadius: '5px'
};
background: '#f5f2f0',
padding: '1em',
margin: '1em 0',
fontSize: 'large',
overflow: 'auto',
borderRadius: '5px',
}
if (inline) {
style.display = 'inline';
style.padding = '0.3em';
style.display = 'inline'
style.padding = '0.3em'
}
Object.keys(style).forEach((rule) => {
pre.style[rule] = style[rule];
});
pre.style[rule] = style[rule]
})
}
}

View file

@ -8,14 +8,16 @@ class HelloWorld extends WebComponent {
count: 0,
}
updateLabel(){
this.props.myName = `Clicked ${++this.props.count}x`;
updateLabel() {
this.props.myName = `Clicked ${++this.props.count}x`
}
get template() {
return html`
<button onClick=${() => this.updateLabel()} style="cursor:pointer">
Hello ${this.props.myName}!
</button>`;
return html` <button
onClick=${() => this.updateLabel()}
style="cursor:pointer"
>
Hello ${this.props.myName}!
</button>`
}
}

View file

@ -1,22 +1,22 @@
class HelloWorld extends HTMLElement {
static get observedAttributes() {
return ["my-name"];
return ['my-name']
}
connectedCallback() {
let count = 0;
const currentName = this.getAttribute('my-name');
let count = 0
const currentName = this.getAttribute('my-name')
if (!currentName) {
this.setAttribute('my-name', 'World')
}
this.onclick = () => this.setAttribute("my-name", `Clicked ${++count}x`);
this.onclick = () => this.setAttribute('my-name', `Clicked ${++count}x`)
}
attributeChangedCallback(property, previousValue, currentValue) {
if (property === "my-name" && previousValue !== currentValue) {
this.innerHTML = `<button style="cursor:pointer">Hello ${currentValue}!</button>`;
if (property === 'my-name' && previousValue !== currentValue) {
this.innerHTML = `<button style="cursor:pointer">Hello ${currentValue}!</button>`
}
}
}

View file

@ -1,4 +1,4 @@
import { defineMcFlyConfig } from "#imports";
import { defineMcFlyConfig } from '#imports'
export default defineMcFlyConfig({
components: "js",
});
components: 'js',
})

View file

@ -1 +1 @@
export default defineNitroConfig({ extends: '@mcflyjs/config' });
export default defineNitroConfig({ extends: '@mcflyjs/config' })

View file

@ -5,5 +5,5 @@
* ...reusable code are in ./src/components
* @see https://ayco.io/gh/McFly#special-directories
*/
import config from "../mcfly.config.mjs";
export default useMcFlyRoute({ config, storage: useStorage() });
import config from '../mcfly.config.mjs'
export default useMcFlyRoute({ config, storage: useStorage() })

View file

@ -5,7 +5,7 @@
*/
export default eventHandler(() => {
return {
user: "AYO",
user: 'AYO',
date: new Date(),
};
});
}
})

View file

@ -1,41 +1,41 @@
class CodeBlockComponent extends HTMLElement {
connectedCallback() {
const trimmed = this.innerHTML.trim();
const lang = this.getAttribute("language");
const inline = this.getAttribute("inline") !== null;
const trimmed = this.innerHTML.trim()
const lang = this.getAttribute('language')
const inline = this.getAttribute('inline') !== null
this.innerHTML = `
<pre id="pre"><code id="code">${trimmed}</code></pre>
`;
`
/**
* @type {HTMLPreElement}
*/
const pre = this.querySelector("#pre");
const pre = this.querySelector('#pre')
if (lang) {
pre.className = `language-${lang}`;
pre.className = `language-${lang}`
}
/**
* @type {Partial<CSSStyleDeclaration>}
*/
const style = {
background: "#f5f2f0",
padding: "1em",
margin: "1em 0",
fontSize: "large",
overflow: "auto",
borderRadius: '5px'
};
background: '#f5f2f0',
padding: '1em',
margin: '1em 0',
fontSize: 'large',
overflow: 'auto',
borderRadius: '5px',
}
if (inline) {
style.display = 'inline';
style.padding = '0.3em';
style.display = 'inline'
style.padding = '0.3em'
}
Object.keys(style).forEach((rule) => {
pre.style[rule] = style[rule];
});
pre.style[rule] = style[rule]
})
}
}

View file

@ -5,7 +5,7 @@
class MyHelloWorld extends WebComponent {
static props = {
myName: 'World',
count: 0
count: 0,
}
updateLabel() {
@ -13,9 +13,11 @@ class MyHelloWorld extends WebComponent {
}
get template() {
return html`
<button onClick=${() => this.updateLabel()} style="cursor:pointer">
Hello ${this.props.myName}!
</button>`;
return html` <button
onClick=${() => this.updateLabel()}
style="cursor:pointer"
>
Hello ${this.props.myName}!
</button>`
}
}