Skip to content

Commit

Permalink
fix: module exports should not be recognized as accessors (carbon-des…
Browse files Browse the repository at this point in the history
  • Loading branch information
metonym authored Feb 13, 2022
1 parent e4c751b commit ae85e72
Show file tree
Hide file tree
Showing 39 changed files with 553 additions and 18 deletions.
156 changes: 156 additions & 0 deletions integration/carbon/COMPONENT_API.json

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions integration/glob/COMPONENT_API.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,39 @@
"reactive": false
}
],
"moduleExports": [
{
"name": "tree",
"kind": "const",
"type": "boolean",
"value": "false",
"isFunction": false,
"isFunctionDeclaration": false,
"constant": true,
"reactive": false
},
{
"name": "computeTreeLeafDepth",
"kind": "function",
"type": "() => any",
"value": "() => { let depth = 0; if (node == null) return depth; let parentNode = node.parentNode; while (parentNode != null && parentNode.getAttribute(\"role\") !== \"tree\") { parentNode = parentNode.parentNode; if (parentNode.tagName === \"LI\") depth++; } return depth; }",
"isFunction": true,
"isFunctionDeclaration": true,
"constant": false,
"reactive": false
},
{
"name": "findParentTreeNode",
"kind": "function",
"description": "Finds the nearest parent tree node",
"type": "(node: HTMLElement) => null | HTMLElement",
"value": "() => { if (node.classList.contains(\"bx--tree-parent-node\")) return node; if (node.classList.contains(\"bx--tree\")) return null; return findParentTreeNode(node.parentNode); }",
"isFunction": true,
"isFunctionDeclaration": true,
"constant": false,
"reactive": false
}
],
"slots": [
{
"name": "__default__",
Expand Down
27 changes: 27 additions & 0 deletions integration/glob/src/button/Button.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,33 @@
<script context="module">
export const tree = false;
export function computeTreeLeafDepth(node) {
let depth = 0;
if (node == null) return depth;
let parentNode = node.parentNode;
while (parentNode != null && parentNode.getAttribute("role") !== "tree") {
parentNode = parentNode.parentNode;
if (parentNode.tagName === "LI") depth++;
}
return depth;
}
/**
* Finds the nearest parent tree node
* @type {(node: HTMLElement) => null | HTMLElement}
*/
export function findParentTreeNode(node) {
if (node.classList.contains("bx--tree-parent-node")) return node;
if (node.classList.contains("bx--tree")) return null;
return findParentTreeNode(node.parentNode);
}
</script>

<script>
export let type = "button2";
export let primary = false;
$: findParentTreeNode(null)
</script>

<button {...$$restProps} {type} class:primary on:click>
Expand Down
9 changes: 9 additions & 0 deletions integration/glob/types/button/Button.svelte.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
/// <reference types="svelte" />
import { SvelteComponentTyped } from "svelte";

export type tree = boolean;

export type computeTreeLeafDepth = () => any;

/**
* Finds the nearest parent tree node
*/
export type findParentTreeNode = (node: HTMLElement) => null | HTMLElement;

export interface ButtonProps
extends svelte.JSX.HTMLAttributes<HTMLElementTagNameMap["button"]> {
/**
Expand Down
7 changes: 6 additions & 1 deletion integration/glob/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
export { default as Action } from "./action/";
export { default as Button } from "./button/Button.svelte";
export {
default as Button,
tree,
computeTreeLeafDepth,
findParentTreeNode,
} from "./button/Button.svelte";
4 changes: 4 additions & 0 deletions integration/multi-export-typed/COMPONENT_API.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"reactive": false
}
],
"moduleExports": [],
"slots": [
{
"name": "__default__",
Expand All @@ -43,6 +44,7 @@
"moduleName": "Link",
"filePath": "src/Link.svelte",
"props": [],
"moduleExports": [],
"slots": [
{
"name": "__default__",
Expand Down Expand Up @@ -80,6 +82,7 @@
"reactive": false
}
],
"moduleExports": [],
"slots": [
{
"name": "__default__",
Expand Down Expand Up @@ -107,6 +110,7 @@
"reactive": false
}
],
"moduleExports": [],
"slots": [
{
"name": "__default__",
Expand Down
4 changes: 4 additions & 0 deletions integration/multi-export/COMPONENT_API.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"reactive": false
}
],
"moduleExports": [],
"slots": [
{
"name": "__default__",
Expand All @@ -52,6 +53,7 @@
"moduleName": "Header",
"filePath": "src/nested/Header.svelte",
"props": [],
"moduleExports": [],
"slots": [{ "name": "__default__", "default": true, "slot_props": "{}" }],
"events": [],
"typedefs": []
Expand All @@ -60,6 +62,7 @@
"moduleName": "Link",
"filePath": "src/Link.svelte",
"props": [],
"moduleExports": [],
"slots": [
{
"name": "__default__",
Expand Down Expand Up @@ -97,6 +100,7 @@
"reactive": false
}
],
"moduleExports": [],
"slots": [
{
"name": "__default__",
Expand Down
1 change: 1 addition & 0 deletions integration/single-export/COMPONENT_API.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"reactive": false
}
],
"moduleExports": [],
"slots": [
{
"name": "__default__",
Expand Down
106 changes: 104 additions & 2 deletions src/ComponentParser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { compile, walk } from "svelte/compiler";
import { compile, walk, parse } from "svelte/compiler";
import * as commentParser from "comment-parser";
import { Ast, TemplateNode, Var } from "svelte/types/compiler/interfaces";
import { getElementByTag } from "./element-tag-map";
Expand Down Expand Up @@ -97,6 +97,7 @@ interface ComponentPropBindings {

export interface ParsedComponent {
props: ComponentProp[];
moduleExports: ComponentProp[];
slots: ComponentSlot[];
events: ComponentEvent[];
typedefs: TypeDef[];
Expand All @@ -109,12 +110,14 @@ export default class ComponentParser {
private options?: ComponentParserOptions;
private source?: string;
private compiled?: CompiledSvelteCode;
private parsed?: Ast;
private rest_props?: RestProps;
private extends?: Extends;
private componentComment?: string;
private readonly reactive_vars: Set<string> = new Set();
private readonly vars: Set<VariableDeclaration> = new Set();
private readonly props: Map<ComponentPropName, ComponentProp> = new Map();
private readonly moduleExports: Map<ComponentPropName, ComponentProp> = new Map();
private readonly slots: Map<ComponentSlotName, ComponentSlot> = new Map();
private readonly events: Map<ComponentEventName, ComponentEvent> = new Map();
private readonly typedefs: Map<TypeDefName, TypeDef> = new Map();
Expand Down Expand Up @@ -171,6 +174,21 @@ export default class ComponentParser {
}
}

private addModuleExport(prop_name: string, data: ComponentProp) {
if (ComponentParser.assignValue(prop_name) === undefined) return;

if (this.moduleExports.has(prop_name)) {
const existing_slot = this.moduleExports.get(prop_name)!;

this.moduleExports.set(prop_name, {
...existing_slot,
...data,
});
} else {
this.moduleExports.set(prop_name, data);
}
}

private aliasType(type: any) {
if (type === "*") return "any";
return type;
Expand Down Expand Up @@ -258,11 +276,13 @@ export default class ComponentParser {
public cleanup() {
this.source = undefined;
this.compiled = undefined;
this.parsed = undefined;
this.rest_props = undefined;
this.extends = undefined;
this.componentComment = undefined;
this.reactive_vars.clear();
this.props.clear();
this.moduleExports.clear();
this.slots.clear();
this.events.clear();
this.typedefs.clear();
Expand All @@ -277,13 +297,94 @@ export default class ComponentParser {
this.cleanup();
this.source = source;
this.compiled = compile(source);
this.parsed = parse(source);
this.collectReactiveVars();
this.parseCustomTypes();

walk(this.parsed?.module, {
enter: (node) => {
if (node.type === "ExportNamedDeclaration") {
const {
type: declaration_type,
id,
init,
body,
} = node.declaration?.declarations ? node.declaration.declarations[0] : node.declaration;

let prop_name = id.name;
let value = undefined;
let type = undefined;
let kind = node.declaration.kind;
let description = undefined;
let isFunction = false;
let isFunctionDeclaration = false;

if (init != null) {
if (
init.type === "ObjectExpression" ||
init.type === "BinaryExpression" ||
init.type === "ArrayExpression" ||
init.type === "ArrowFunctionExpression"
) {
value = this.sourceAtPos(init.start, init.end)?.replace(/\n/g, " ");
type = value;
isFunction = init.type === "ArrowFunctionExpression";

if (init.type === "BinaryExpression") {
if (init?.left.type === "Literal" && typeof init?.left.value === "string") {
type = "string";
}
}
} else {
if (init.type === "UnaryExpression") {
value = this.sourceAtPos(init.start, init.end);
type = typeof init.argument?.value;
} else {
value = init.raw;
type = init.value == null ? undefined : typeof init.value;
}
}
}

if (declaration_type === "FunctionDeclaration") {
value = "() => " + this.sourceAtPos(body.start, body.end)?.replace(/\n/g, " ");
type = "() => any";
kind = "function";
isFunction = true;
isFunctionDeclaration = true;
}

if (node.leadingComments) {
const last_comment = node.leadingComments[node.leadingComments.length - 1];
const comment = commentParser(ComponentParser.formatComment(last_comment.value));
const tag = comment[0]?.tags[comment[0]?.tags.length - 1];
if (tag?.tag === "type") type = this.aliasType(tag.type);
description = ComponentParser.assignValue(comment[0]?.description);
}

if (!description && this.typedefs.has(type)) {
description = this.typedefs.get(type)!.description;
}

this.addModuleExport(prop_name, {
name: prop_name,
kind,
description,
type,
value,
isFunction,
isFunctionDeclaration,
constant: kind === "const",
reactive: false,
});
}
},
});

let dispatcher_name: undefined | string = undefined;
let callees: { name: string; arguments: any }[] = [];

walk(this.compiled.ast as unknown as Node, {
walk({ html: this.parsed.html, instance: this.parsed.instance } as unknown as Node, {
enter: (node, parent, prop) => {
if (node.type === "CallExpression") {
if (node.callee.name === "createEventDispatcher") {
Expand Down Expand Up @@ -519,6 +620,7 @@ export default class ComponentParser {

return prop;
}),
moduleExports: ComponentParser.mapToArray(this.moduleExports),
slots: ComponentParser.mapToArray(this.slots)
.map((slot) => {
try {
Expand Down
20 changes: 16 additions & 4 deletions src/create-exports.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
import { ParsedExports } from "./parse-exports";
import { ComponentDocs } from "./rollup-plugin";

export function createExports(parsed_exports: ParsedExports): string {
export function createExports(parsed_exports: ParsedExports, components: ComponentDocs): string {
const source = Object.entries(parsed_exports).map(([id, exportee]) => {
let module_exports: string[] = [];
if (components.has(id)) {
module_exports = components.get(id)!.moduleExports.map((moduleExport) => {
return moduleExport.name;
});
}

let named_exports = "";

if (module_exports.length > 0) named_exports = ", " + module_exports.join(", ");

if (id === "default" || exportee.default) {
if (exportee.mixed) {
return `export { default as ${id} } from "${exportee.source}";\nexport { default } from "${exportee.source}";`;
return `export { default as ${id}${named_exports} } from "${exportee.source}";\nexport { default } from "${exportee.source}";`;
}

return `export { default } from "${exportee.source}";`;
return `export { default${named_exports} } from "${exportee.source}";`;
}

return `export { default as ${id} } from "${exportee.source}";`;
return `export { default as ${id}${named_exports} } from "${exportee.source}";`;
});

return source.join("\n");
Expand Down
Loading

0 comments on commit ae85e72

Please sign in to comment.