-
-
Notifications
You must be signed in to change notification settings - Fork 837
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
437e022
commit 9c4daeb
Showing
2 changed files
with
232 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
import * as path from 'path'; | ||
import { customElementJetBrainsPlugin } from 'custom-element-jet-brains-integration'; | ||
import { customElementVsCodePlugin } from 'custom-element-vs-code-integration'; | ||
import { customElementVuejsPlugin } from 'custom-element-vuejs-integration'; | ||
import { parse } from 'comment-parser'; | ||
import { pascalCase } from 'pascal-case'; | ||
import commandLineArgs from 'command-line-args'; | ||
import fs from 'fs'; | ||
|
||
const packageData = JSON.parse(fs.readFileSync('./package.json', 'utf8')); | ||
const { name, description, version, author, homepage, license } = packageData; | ||
|
||
const { outdir } = commandLineArgs([ | ||
{ name: 'litelement', type: String }, | ||
{ name: 'analyze', defaultOption: true }, | ||
{ name: 'outdir', type: String } | ||
]); | ||
|
||
function noDash(string) { | ||
return string.replace(/^\s?-/, '').trim(); | ||
} | ||
|
||
function replace(string, terms) { | ||
terms.forEach(({ from, to }) => { | ||
string = string?.replace(from, to); | ||
}); | ||
|
||
return string; | ||
} | ||
|
||
export default { | ||
globs: ['src/components/**/*.component.ts'], | ||
exclude: ['**/*.styles.ts', '**/*.test.ts'], | ||
plugins: [ | ||
// Append package data | ||
{ | ||
name: 'shoelace-package-data', | ||
packageLinkPhase({ customElementsManifest }) { | ||
customElementsManifest.package = { name, description, version, author, homepage, license }; | ||
} | ||
}, | ||
|
||
// Infer tag names because we no longer use @customElement decorators. | ||
{ | ||
name: 'shoelace-infer-tag-names', | ||
analyzePhase({ ts, node, moduleDoc }) { | ||
switch (node.kind) { | ||
case ts.SyntaxKind.ClassDeclaration: { | ||
const className = node.name.getText(); | ||
const classDoc = moduleDoc?.declarations?.find(declaration => declaration.name === className); | ||
|
||
const importPath = moduleDoc.path; | ||
|
||
// This is kind of a best guess at components. "thing.component.ts" | ||
if (!importPath.endsWith('.component.ts')) { | ||
return; | ||
} | ||
|
||
const tagNameWithoutPrefix = path.basename(importPath, '.component.ts'); | ||
const tagName = 'sl-' + tagNameWithoutPrefix; | ||
|
||
classDoc.tagNameWithoutPrefix = tagNameWithoutPrefix; | ||
classDoc.tagName = tagName; | ||
|
||
// This used to be set to true by @customElement | ||
classDoc.customElement = true; | ||
} | ||
} | ||
} | ||
}, | ||
|
||
// Parse custom jsDoc tags | ||
{ | ||
name: 'shoelace-custom-tags', | ||
analyzePhase({ ts, node, moduleDoc }) { | ||
switch (node.kind) { | ||
case ts.SyntaxKind.ClassDeclaration: { | ||
const className = node.name.getText(); | ||
const classDoc = moduleDoc?.declarations?.find(declaration => declaration.name === className); | ||
const customTags = ['animation', 'dependency', 'documentation', 'since', 'status', 'title']; | ||
let customComments = '/**'; | ||
|
||
node.jsDoc?.forEach(jsDoc => { | ||
jsDoc?.tags?.forEach(tag => { | ||
const tagName = tag.tagName.getText(); | ||
|
||
if (customTags.includes(tagName)) { | ||
customComments += `\n * @${tagName} ${tag.comment}`; | ||
} | ||
}); | ||
}); | ||
|
||
// This is what allows us to map JSDOC comments to ReactWrappers. | ||
classDoc['jsDoc'] = node.jsDoc?.map(jsDoc => jsDoc.getFullText()).join('\n'); | ||
|
||
const parsed = parse(`${customComments}\n */`); | ||
parsed[0].tags?.forEach(t => { | ||
switch (t.tag) { | ||
// Animations | ||
case 'animation': | ||
if (!Array.isArray(classDoc['animations'])) { | ||
classDoc['animations'] = []; | ||
} | ||
classDoc['animations'].push({ | ||
name: t.name, | ||
description: noDash(t.description) | ||
}); | ||
break; | ||
|
||
// Dependencies | ||
case 'dependency': | ||
if (!Array.isArray(classDoc['dependencies'])) { | ||
classDoc['dependencies'] = []; | ||
} | ||
classDoc['dependencies'].push(t.name); | ||
break; | ||
|
||
// Value-only metadata tags | ||
case 'documentation': | ||
case 'since': | ||
case 'status': | ||
case 'title': | ||
classDoc[t.tag] = t.name; | ||
break; | ||
|
||
// All other tags | ||
default: | ||
if (!Array.isArray(classDoc[t.tag])) { | ||
classDoc[t.tag] = []; | ||
} | ||
|
||
classDoc[t.tag].push({ | ||
name: t.name, | ||
description: t.description, | ||
type: t.type || undefined | ||
}); | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
}, | ||
|
||
{ | ||
name: 'shoelace-react-event-names', | ||
analyzePhase({ ts, node, moduleDoc }) { | ||
switch (node.kind) { | ||
case ts.SyntaxKind.ClassDeclaration: { | ||
const className = node.name.getText(); | ||
const classDoc = moduleDoc?.declarations?.find(declaration => declaration.name === className); | ||
|
||
if (classDoc?.events) { | ||
classDoc.events.forEach(event => { | ||
event.reactName = `on${pascalCase(event.name)}`; | ||
event.eventName = `${pascalCase(event.name)}Event`; | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
|
||
{ | ||
name: 'shoelace-translate-module-paths', | ||
packageLinkPhase({ customElementsManifest }) { | ||
customElementsManifest?.modules?.forEach(mod => { | ||
// | ||
// CEM paths look like this: | ||
// | ||
// src/components/button/button.ts | ||
// | ||
// But we want them to look like this: | ||
// | ||
// components/button/button.js | ||
// | ||
const terms = [ | ||
{ from: /^src\//, to: '' }, // Strip the src/ prefix | ||
{ from: /\.component.(t|j)sx?$/, to: '.js' } // Convert .ts to .js | ||
]; | ||
|
||
mod.path = replace(mod.path, terms); | ||
|
||
for (const ex of mod.exports ?? []) { | ||
ex.declaration.module = replace(ex.declaration.module, terms); | ||
} | ||
|
||
for (const dec of mod.declarations ?? []) { | ||
if (dec.kind === 'class') { | ||
for (const member of dec.members ?? []) { | ||
if (member.inheritedFrom) { | ||
member.inheritedFrom.module = replace(member.inheritedFrom.module, terms); | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
}, | ||
|
||
// Generate custom VS Code data | ||
customElementVsCodePlugin({ | ||
outdir, | ||
cssFileName: null, | ||
referencesTemplate: (_, tag) => [ | ||
{ | ||
name: 'Documentation', | ||
url: `https://shoelace.style/components/${tag.replace('sl-', '')}` | ||
} | ||
] | ||
}), | ||
|
||
customElementJetBrainsPlugin({ | ||
outdir: './dist', | ||
excludeCss: true, | ||
packageJson: false, | ||
referencesTemplate: (_, tag) => { | ||
return { | ||
name: 'Documentation', | ||
url: `https://shoelace.style/components/${tag.replace('sl-', '')}` | ||
}; | ||
} | ||
}), | ||
|
||
customElementVuejsPlugin({ | ||
outdir: './dist/types/vue', | ||
fileName: 'index.d.ts', | ||
componentTypePath: (_, tag) => `../../components/${tag.replace('sl-', '')}/${tag.replace('sl-', '')}.component.js` | ||
}) | ||
] | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters