Skip to content

Commit

Permalink
Do not require a package.json anymore.
Browse files Browse the repository at this point in the history
  • Loading branch information
edoardocavazza committed Dec 14, 2023
1 parent ad1927f commit 725c6b4
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 79 deletions.
5 changes: 5 additions & 0 deletions .changeset/lemon-bottles-act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@chialab/plasma': minor
---

Do not require a package.json anymore.
12 changes: 4 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ Generate Custom Elements wrappers for Angular, React, Svelte and Vue.

Plasma transformations are based on [Custom Element Manifest](https://github.com/webcomponents/custom-elements-manifest) (CEM) specifications. The CEM is a JSON files that describes a Custom Element, its properties, events and slots. Plasma uses the CEM to generate wrappers for the supported frameworks.

In order to run the `plasma` cli you need:

- a `package.json` file. Plasma uses the `name` field to import the Custom Element definition and the `customElements` field to detect the CEM file;
- a CEM file. You can generate the CEM for most of the Web Components library using this [Analyzer](https://github.com/open-wc/custom-elements-manifest/tree/master/packages/analyzer).
You can generate the CEM for most of the Web Components library using this [Analyzer](https://github.com/open-wc/custom-elements-manifest/tree/master/packages/analyzer).

## Installation

Expand All @@ -40,19 +37,18 @@ npm run plasma
### Options

```
npm run plasma --help
Usage: plasma [options] <input>
Generate Custom Elements wrappers for Angular, React, Svelte and Vue.
Arguments:
input source directory
input custom elements manifest path
Options:
-V, --version output the version number
-f, --frameworks <frameworks...> the framework to convert to
-e, --entrypoint <path> entrypoint to the package
-o, --outdir <outdir> output directory
-f, --frameworks <frameworks...> the framework to convert to
-y, --yes convert all candidates to all available frameworks
-h, --help display help for command
```
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"build": "yarn clear && tsc --project tsconfig.build.json && node scripts/build.js",
"lint": "prettier --check . && eslint .",
"test:manifest": "custom-elements-manifest analyze --config ./test/cem.config.mjs --outdir test",
"test:generate": "./dist/cli.js test --outdir 'test/src/[framework]' -y",
"test:generate": "./dist/cli.js test --entrypoint 'plasma-test' --outdir 'test/src/[framework]' -y",
"test": "vitest",
"prepack": "yarn build && publint"
},
Expand All @@ -65,7 +65,6 @@
"eslint": "^8.0.0",
"jsdom": "^23.0.0",
"listr2": "^7.0.2",
"package-up": "^5.0.0",
"preact": "^10.19.2",
"prettier": "^3.0.0",
"prompts": "^2.4.0",
Expand Down
21 changes: 8 additions & 13 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import { fileURLToPath } from 'node:url';
import chalk from 'chalk';
import { program } from 'commander';
import { Listr } from 'listr2';
import { packageUp } from 'package-up';
import prompts from 'prompts';
import { candidates, SUPPORTED, transform, UNSUPPORTED, type Frameworks } from './index';
import { parseManifestFromPackage, parsePackageJson } from './parser';
import { findManifest } from './utils';

const colorFramework = (framework: string) => {
switch (framework) {
Expand All @@ -35,36 +34,31 @@ program
.description(json.description)
.version(json.version)

.argument('[input]', 'source directory')
.option('-f, --frameworks <frameworks...>', 'the framework to convert to')
.argument('[input]', 'custom elements manifest path')
.requiredOption('-e, --entrypoint <path>', 'entrypoint to the package')
.requiredOption('-o, --outdir <outdir>', 'output directory')
.option('-f, --frameworks <frameworks...>', 'the framework to convert to')
.option('-y, --yes', 'convert all candidates to all available frameworks')

.action(
async (
sourceDir,
options: {
outdir: string;
entrypoint?: string;
entrypoint: string;
frameworks?: Frameworks[];
yes?: boolean;
}
) => {
sourceDir = sourceDir ? resolve(sourceDir) : process.cwd();

const input = await packageUp({ cwd: sourceDir });
if (!input) {
throw new Error('No package.json found');
}

const json = await parsePackageJson(input);
const manifest = await parseManifestFromPackage(input, json);
const manifest = await findManifest(sourceDir);
if (!manifest) {
throw new Error('No custom elements manifest found');
}

const yes = options.yes || !process.stdout.isTTY;
const data = Array.from(candidates(json, manifest));
const data = Array.from(candidates(manifest));
if (data.length === 0) {
throw new Error('No components found');
}
Expand Down Expand Up @@ -135,6 +129,7 @@ program

const outDir = options.outdir.replace(/\[framework\]/g, framework);
const outFile = await transform(entry, framework, {
entrypoint: options.entrypoint,
outdir: outDir,
});
task.title = `Converted ${chalk.whiteBright(component)} to ${colorFramework(
Expand Down
27 changes: 0 additions & 27 deletions src/parser.ts

This file was deleted.

21 changes: 13 additions & 8 deletions src/preact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ import { capitalize, filterPublicMemebers } from './utils';
import type { Entry } from './walker';

export interface PreactTransformOptions {
/**
* The entrypoint to the package.
*/
entrypoint: string;

/**
* The output directory to write the converted components to.
*/
outdir: string;
}

export function generatePreactComponent(entry: Entry) {
const { packageJson, definition, declaration } = entry;
export function generatePreactComponent(entry: Entry, options: PreactTransformOptions) {
const { definition, declaration } = entry;

const props = filterPublicMemebers(declaration).map((member) => member.name);
const eventProps =
Expand All @@ -25,7 +30,7 @@ export function generatePreactComponent(entry: Entry) {

return `import { h } from 'preact';
import { useRef, useEffect } from 'preact/hooks';
import '${packageJson.name}';
import '${options.entrypoint}';
const properties = ${JSON.stringify(props)};
const events = ${JSON.stringify(eventProps)};
Expand Down Expand Up @@ -62,11 +67,11 @@ export const ${declaration.name} = ({ children, ...props }) => {
};`;
}

export function generatePreactTypings(entry: Entry) {
const { packageJson, declaration } = entry;
export function generatePreactTypings(entry: Entry, options: PreactTransformOptions) {
const { declaration } = entry;

const imports = `import { FunctionComponent, JSX } from 'preact';
import { ${declaration.name} as Base${declaration.name} } from '${packageJson.name}';
import { ${declaration.name} as Base${declaration.name} } from '${options.entrypoint}';
`;

const propertiesTypings = filterPublicMemebers(declaration).map(
Expand Down Expand Up @@ -95,8 +100,8 @@ export async function transformPreact(entry: Entry, options: PreactTransformOpti
recursive: true,
});
await Promise.all([
writeFile(outFile, generatePreactComponent(entry)),
writeFile(declFile, generatePreactTypings(entry)),
writeFile(outFile, generatePreactComponent(entry, options)),
writeFile(declFile, generatePreactTypings(entry, options)),
]);

return outFile;
Expand Down
21 changes: 13 additions & 8 deletions src/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import { capitalize, filterPublicMemebers } from './utils';
import type { Entry } from './walker';

export interface ReactTransformOptions {
/**
* The entrypoint to the package.
*/
entrypoint: string;

/**
* The output directory to write the converted components to.
*/
Expand Down Expand Up @@ -163,8 +168,8 @@ function getAttributes(tagName: string) {
}
}

export function generateReactComponent(entry: Entry) {
const { packageJson, definition, declaration } = entry;
export function generateReactComponent(entry: Entry, options: ReactTransformOptions) {
const { definition, declaration } = entry;

const props = filterPublicMemebers(declaration).map((member) => member.name);
const eventProps =
Expand All @@ -177,7 +182,7 @@ export function generateReactComponent(entry: Entry) {
) ?? {};

return `import React, { useRef, useEffect } from 'react';
import '${packageJson.name}';
import '${options.entrypoint}';
const properties = ${JSON.stringify(props)};
const events = ${JSON.stringify(eventProps)};
Expand Down Expand Up @@ -214,11 +219,11 @@ export const ${declaration.name} = ({ children, ...props }) => {
};`;
}

export function generateReactTypings(entry: Entry) {
const { packageJson, definition, declaration } = entry;
export function generateReactTypings(entry: Entry, options: ReactTransformOptions) {
const { definition, declaration } = entry;

const imports = `import React from 'react';
import { ${declaration.name} as Base${declaration.name} } from '${packageJson.name}';
import { ${declaration.name} as Base${declaration.name} } from '${options.entrypoint}';
`;

const propertiesTypings = filterPublicMemebers(declaration).map(
Expand Down Expand Up @@ -250,8 +255,8 @@ export async function transformReact(entry: Entry, options: ReactTransformOption
recursive: true,
});
await Promise.all([
writeFile(outFile, generateReactComponent(entry)),
writeFile(declFile, generateReactTypings(entry)),
writeFile(outFile, generateReactComponent(entry, options)),
writeFile(declFile, generateReactTypings(entry, options)),
]);

return outFile;
Expand Down
21 changes: 13 additions & 8 deletions src/svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import { filterPublicMemebers } from './utils';
import type { Entry } from './walker';

export interface SvelteTransformOptions {
/**
* The entrypoint to the package.
*/
entrypoint: string;

/**
* The output directory to write the converted components to.
*/
Expand Down Expand Up @@ -163,8 +168,8 @@ function getAttributes(tagName: string) {
}
}

export function generateSvelteComponent(entry: Entry) {
const { packageJson, definition, declaration } = entry;
export function generateSvelteComponent(entry: Entry, options: SvelteTransformOptions) {
const { definition, declaration } = entry;

const props: string[] = [];
const setters: string[] = [];
Expand Down Expand Up @@ -196,7 +201,7 @@ export function generateSvelteComponent(entry: Entry) {

return `<script>
import { onMount, bubble, listen, get_current_component } from 'svelte/internal';
import '${packageJson.name}';
import '${options.entrypoint}';
let __ref;
let __mounted = false;
Expand Down Expand Up @@ -245,10 +250,10 @@ export function generateSvelteComponent(entry: Entry) {
${markup}`;
}

export function generateSvelteTypings(entry: Entry) {
const { packageJson, definition, declaration } = entry;
export function generateSvelteTypings(entry: Entry, options: SvelteTransformOptions) {
const { definition, declaration } = entry;
const imports = `import { SvelteComponent } from 'svelte';
import { ${declaration.name} as Base${declaration.name} } from '${packageJson.name}';
import { ${declaration.name} as Base${declaration.name} } from '${options.entrypoint}';
import { ${getAttributes(definition.extend ?? definition.name).split('<')[0]} } from 'svelte/elements';
`;

Expand Down Expand Up @@ -310,8 +315,8 @@ export async function transformSvelte(entry: Entry, options: SvelteTransformOpti
outFile,
`import ${declaration.name} from './${declaration.name}.svelte';\n\nexport { ${declaration.name} };`
),
writeFile(svelteFile, generateSvelteComponent(entry)),
writeFile(declFile, generateSvelteTypings(entry)),
writeFile(svelteFile, generateSvelteComponent(entry, options)),
writeFile(declFile, generateSvelteTypings(entry, options)),
]);

return outFile;
Expand Down
31 changes: 30 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { ClassField, CustomElementDeclaration } from 'custom-elements-manifest';
import { access, readFile } from 'node:fs/promises';
import { dirname, extname, join } from 'node:path';
import type { ClassField, CustomElementDeclaration, Package } from 'custom-elements-manifest';

export function capitalize(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1);
Expand Down Expand Up @@ -28,3 +30,30 @@ export function isOptionalClassField(member: ClassField) {
.includes('undefined') ?? true
);
}

export async function parseJson(fileName: string) {
const contents = await readFile(fileName, 'utf-8');
const json = JSON.parse(contents);

return json;
}

export async function findManifest(from: string, name = 'custom-elements.json') {
if (extname(from) === '.json') {
return parseJson(from) as Promise<Package>;
}

const packageJsonFile = join(from, name);
try {
await access(packageJsonFile);

return parseJson(packageJsonFile) as Promise<Package>;
} catch {
const dir = dirname(from);
if (dir === from) {
throw new Error(`No ${name} found`);
}

return findManifest(dir, name);
}
}
6 changes: 2 additions & 4 deletions src/walker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ import type {
JavaScriptModule,
Package,
} from 'custom-elements-manifest';
import type { PackageJson } from './parser';

export interface Entry {
packageJson: PackageJson;
manifest: Package;
module: JavaScriptModule;
definition: CustomElementExport & { extend?: string };
Expand All @@ -19,7 +17,7 @@ function isCustomElementDeclaration(declaration: Declaration): declaration is Cu
return declaration.kind === 'class' && 'customElement' in declaration;
}

export function* candidates(packageJson: PackageJson, manifest: Package): Generator<Entry> {
export function* candidates(manifest: Package): Generator<Entry> {
for (const module of manifest.modules) {
if (module.exports) {
for (const export_ of module.exports) {
Expand All @@ -34,7 +32,7 @@ export function* candidates(packageJson: PackageJson, manifest: Package): Genera
if (declaration.tagName !== export_.name) {
continue;
}
yield { packageJson, manifest, module, definition: export_, declaration };
yield { manifest, module, definition: export_, declaration };
}
}
}
Expand Down

0 comments on commit 725c6b4

Please sign in to comment.