Skip to content

Commit

Permalink
add new plugins for vscode custom data
Browse files Browse the repository at this point in the history
  • Loading branch information
janechu committed Jul 27, 2024
1 parent 7d2ee97 commit ed1b59b
Show file tree
Hide file tree
Showing 17 changed files with 248 additions and 26 deletions.
10 changes: 10 additions & 0 deletions packages/web-components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ import { ButtonDefinition, FluentDesignSystem } from '@fluentui/web-components';
ButtonDefinition.define(FluentDesignSystem.registry);
```

## Developer Experience

For convenience we have included a [CEM (custom elements manifest)](https://github.com/webcomponents/custom-elements-manifest).

```js
import CEM from '@fluentui/custom-elements.json';
```

We have also included an [HTML custom data file for VS Code](./vs-code.md).

## Development

To start the component development environment, run `yarn start`.
Expand Down
11 changes: 9 additions & 2 deletions packages/web-components/custom-elements-manifest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { tagNameFix } from "./custom-elements-manifest.plugins.js";
import { tagNameFix, typescriptTypeTextSanitize } from "./custom-elements-manifest.plugins.js";
import { customElementVsCodePlugin } from "custom-element-vs-code-integration";

export default {
/** Globs to analyze */
Expand All @@ -24,5 +25,11 @@ export default {
dev: false,
/** Enable special handling for fast */
fast: true,
plugins: [tagNameFix()],
plugins: [
tagNameFix(),
typescriptTypeTextSanitize(),
customElementVsCodePlugin({
outdir: "dist"
})
],
};
154 changes: 147 additions & 7 deletions packages/web-components/custom-elements-manifest.plugins.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,89 @@
const pascalToKebab = (input) => {
return input
.split(/\.?(?=[A-Z])/)
.join('-')
.toLowerCase();
};
import { readFileSync } from "fs";
import path from "path";
import ts from "typescript";
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const getTagNameFromCommentInDefinitionFile = function (definitionPathName) {
const indexFilePath = path.resolve(__dirname, `./${definitionPathName}`);
let name;

try {
let sourceFile = ts.createSourceFile(
indexFilePath,
readFileSync(indexFilePath).toString(),
ts.ScriptTarget.ES2015,
/*setParentNodes */ true
);

if (Array.isArray(sourceFile.statements)) {
sourceFile.statements.forEach((statement) => {
if (Array.isArray(statement.jsDoc) && statement.jsDoc[0].tags !== undefined) {
statement.jsDoc.forEach((jsDoc) => {
if (Array.isArray(jsDoc.tags)) {
jsDoc.tags.forEach((tag) => {
if (typeof tag.comment === "string" && tag.comment.startsWith("HTML Element:")) {
name = tag.comment.match(/<(.*)>/)[1].replace("\\", "");
}
});
}
})
}
})
}
} catch (err) {
// do nothing
}

return name;
}

const resolveDefinitionFilePath = function(filePathName) {
return filePathName.split("/").map((pathItem) => {
if (pathItem.endsWith(".ts")) {
const splitPathItem = pathItem.split(".");
splitPathItem.splice(1, 0, "definition");
return splitPathItem.join(".");
}

return pathItem;
}).join("/");
}

const checkIsUnresolvedTypeScriptType = function(type) {
/**
* Due to TypeScript types being PascalCase, and all other default
* types being lowercase, we determine if this is a typescript type by checking
* the first letter.
*/
return type[0] === type[0].toUpperCase() && isNaN(type[0]);
}

const resolveTypeToValues = function(CEM, type) {
let values = "";

CEM.modules.forEach((cemModule) => {
if (cemModule.kind === "javascript-module") {
cemModule.declarations.forEach((declaration) => {
if (declaration.name === type) {
const sanitizedType = declaration.type?.text;
const matches = sanitizedType
.match(/((?:.*):(?:.*))/gm)
.map((match) => {
return match.match(/(?:(?:')(.*)(?:')|(\d+))/)[0];
});
values = matches.reduce((accum, match) => {
return `${accum}${accum === "" ? "" : " | "}${match}`;
}, values)
}
})
}
});

return values;
}

/**
* @return {import('@custom-elements-manifest/analyzer').Plugin}
Expand All @@ -18,7 +98,67 @@ export function tagNameFix() {
customElementsManifest.modules.map((item) => {
item.declarations.forEach((declaration) => {
if (declaration.customElement) {
declaration.tagName = `fluent-${pascalToKebab(declaration.name)}`;
const name = getTagNameFromCommentInDefinitionFile(
resolveDefinitionFilePath(item.path)
);

if (typeof name === "undefined") {
console.error(`no tag name for ${item.path}`);
} else {
declaration.tagName = name;
}
}
})
});
},
};
}

/**
* @return {import('@custom-elements-manifest/analyzer').Plugin}
*
* This plugin changes the types to use pipe syntax so that the vscode plugin can
* correctly interpret the possible values, eg.
* from:
* {
* "name": "heading-level",
* "type": {
* "text": "HeadingLevel"
* },
* "fieldName": "headinglevel"
* }
*
* to:
* {
* "name": "heading-level",
* "type": {
* "text": "1 | 2 | 3 | 4 | 5 | 6"
* },
* "fieldName": "headinglevel"
* },
*/
export function typescriptTypeTextSanitize() {
return {
name: "typescriptTypeTextSanitize",
packageLinkPhase({customElementsManifest, context}){
customElementsManifest.modules.map((item) => {
item.declarations.forEach((declaration) => {
if (declaration.customElement && Array.isArray(declaration.attributes)) {
declaration.attributes.forEach((attribute) => {
if (typeof attribute.type?.text === "string") {
const possibleTypes = attribute.type.text.split("|").map((item) => {
return item.trim();
}).map((possibleType) => {
if (checkIsUnresolvedTypeScriptType(possibleType)) {
return resolveTypeToValues(customElementsManifest, possibleType);
}

return possibleType;
}).join(" | ");

attribute.type.text = possibleTypes;
}
});
}
})
});
Expand Down
8 changes: 5 additions & 3 deletions packages/web-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"dist/dts/",
"dist/esm/",
"dist/*.js",
"dist/*.d.ts"
"dist/*.d.ts",
"dist/*.json"
],
"exports": {
".": {
Expand Down Expand Up @@ -248,10 +249,11 @@
"devDependencies": {
"@microsoft/fast-element": "2.0.0-beta.26",
"@tensile-perf/web-components": "~0.2.0",
"@custom-elements-manifest/analyzer": "^0.10.2",
"@custom-elements-manifest/analyzer": "9.8.0",
"@types/web": "^0.0.142",
"@storybook/html": "6.5.15",
"chromedriver": "^125.0.0"
"chromedriver": "^125.0.0",
"custom-element-vs-code-integration": "^1.4.0"
},
"dependencies": {
"@microsoft/fast-web-utilities": "^6.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/web-components/src/avatar/avatar.definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { template } from './avatar.template.js';
*
* @public
* @remarks
* HTML Element: \<fluent-badge\>
* HTML Element: \<fluent-avatar\>
*/
export const definition = Avatar.compose({
name: `${FluentDesignSystem.prefix}-avatar`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { template } from './compound-button.template.js';
/**
* @public
* @remarks
* HTML Element: \<fluent-comopund-button\>
* HTML Element: \<fluent-compound-button\>
*/
export const definition = CompoundButton.compose({
name: `${FluentDesignSystem.prefix}-compound-button`,
Expand Down
6 changes: 4 additions & 2 deletions packages/web-components/src/divider/divider.options.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Orientation } from '@microsoft/fast-web-utilities';
import type { ValuesOf } from '../utils/index.js';

/**
Expand Down Expand Up @@ -27,7 +26,10 @@ export type DividerRole = ValuesOf<typeof DividerRole>;
* Divider orientation
* @public
*/
export const DividerOrientation = Orientation;
export const DividerOrientation = {
horizontal: 'horizontal',
vertical: 'vertical',
} as const;

/**
* The types for Divider orientation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { template } from './drawer-body.template.js';
*
* @public
* @remarks
* HTML Element: <fluent-drawer>
* HTML Element: <fluent-drawer-body>
*/

export const definition = DrawerBody.compose({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { template } from './menu-button.template.js';
/**
* @public
* @remarks
* HTML Element: \<fluent-button\>
* HTML Element: \<fluent-menu-button\>
*/
export const definition = MenuButton.compose({
name: `${FluentDesignSystem.prefix}-menu-button`,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Orientation } from '@microsoft/fast-web-utilities';
import type { ValuesOf } from '../utils/index.js';

/**
* Radio Group orientation
* @public
*/
export const RadioGroupOrientation = Orientation;
export const RadioGroupOrientation = {
horizontal: 'horizontal',
vertical: 'vertical',
} as const;

/**
* Types of Radio Group orientation
Expand Down
7 changes: 7 additions & 0 deletions packages/web-components/src/tab-panel/tab-panel.definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ import { TabPanel } from './tab-panel.js';
import { template } from './tab-panel.template.js';
import { styles } from './tab-panel.styles.js';

/**
* The definition for the Fluent Tab Panel component.
*
* @public
* @remarks
* HTML Element: `<fluent-tab-panel>`
*/
export const definition = TabPanel.compose({
name: `${FluentDesignSystem.prefix}-tab-panel`,
template,
Expand Down
7 changes: 7 additions & 0 deletions packages/web-components/src/tab/tab.definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ import { Tab } from './tab.js';
import { template } from './tab.template.js';
import { styles } from './tab.styles.js';

/**
* The definition for the Fluent Tab component.
*
* @public
* @remarks
* HTML Element: `<fluent-tab>`
*/
export const definition = Tab.compose({
name: `${FluentDesignSystem.prefix}-tab`,
template,
Expand Down
7 changes: 7 additions & 0 deletions packages/web-components/src/tabs/tabs.definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ import { Tabs } from './tabs.js';
import { template } from './tabs.template.js';
import { styles } from './tabs.styles.js';

/**
* The definition for the Fluent Tabs component.
*
* @public
* @remarks
* HTML Element: `<fluent-tabs>`
*/
export const definition = Tabs.compose({
name: `${FluentDesignSystem.prefix}-tabs`,
template,
Expand Down
6 changes: 4 additions & 2 deletions packages/web-components/src/tabs/tabs.options.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Orientation } from '@microsoft/fast-web-utilities';
import { StartEndOptions } from '../patterns/index.js';
import type { ValuesOf } from '../utils/index.js';
import { Tabs } from './tabs.js';
Expand Down Expand Up @@ -28,7 +27,10 @@ export type TabsOptions = StartEndOptions<Tabs>;
* The orientation of the component
* @public
*/
export const TabsOrientation = Orientation;
export const TabsOrientation = {
horizontal: 'horizontal',
vertical: 'vertical',
} as const;

/**
* The types for the Tabs component
Expand Down
17 changes: 17 additions & 0 deletions packages/web-components/vs-code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Using VS Code

As an additional helper included in the `@fluentui/web-components` package is HTML custom data.

![VS Code Custom Data user experience](./vscode-custom-html-data.gif)

To use this, reference the custom data file in your VS Code settings:

```json
{
"html.customData": ["../node_modules/@fluentui/web-components/dist/vscode.html-custom-data.json"]
}
```

**Note:** The path is relative to the root of the project, not the settings file.

Once it has been added, you will need to restart VS Code in order for it to register the new components.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit ed1b59b

Please sign in to comment.