-
Notifications
You must be signed in to change notification settings - Fork 32
GAUD-10267: add transform to fix wca #7219
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| import { spawn } from 'node:child_process'; | ||
| import { glob as globFiles, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; | ||
| import { tmpdir } from 'node:os'; | ||
| import { dirname, join, resolve, relative } from 'node:path'; | ||
|
|
||
| const customElementsFileName = 'custom-elements.json'; | ||
|
|
||
| function findObjectEnd(content, start) { | ||
| let depth = 0; | ||
| let mode = 'code'; | ||
| let quote; | ||
|
|
||
| for (let index = start; index < content.length; index++) { | ||
| const character = content[index]; | ||
| const nextCharacter = content[index + 1]; | ||
|
|
||
| if (mode === 'line-comment') { | ||
| if (character === '\n') mode = 'code'; | ||
| continue; | ||
| } | ||
| if (mode === 'block-comment') { | ||
| if (character === '*' && nextCharacter === '/') { | ||
| mode = 'code'; | ||
| index++; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
| continue; | ||
| } | ||
| if (mode === 'string' || mode === 'template') { | ||
| if (character === '\\') { | ||
| index++; | ||
| } else if (character === quote) { | ||
| mode = 'code'; | ||
| } | ||
| continue; | ||
| } | ||
|
|
||
| if (character === '/' && nextCharacter === '/') { | ||
| mode = 'line-comment'; | ||
| index++; | ||
| } else if (character === '/' && nextCharacter === '*') { | ||
| mode = 'block-comment'; | ||
| index++; | ||
| } else if (character === '\'' || character === '"' || character === '`') { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Took forever to figure this part out, cause I didn't see the second |
||
| mode = character === '`' ? 'template' : 'string'; | ||
| quote = character; | ||
| } else if (character === '{') { | ||
| depth++; | ||
| } else if (character === '}' && --depth === 0) { | ||
| return index; | ||
| } | ||
| } | ||
|
|
||
| throw new Error('Could not find the end of a static properties object.'); | ||
| } | ||
|
|
||
| function transformProperties(content) { | ||
| const propertyPattern = /\bstatic\s+properties\s*=/g; | ||
| let result = ''; | ||
| let sourceIndex = 0; | ||
| let match; | ||
|
|
||
| while ((match = propertyPattern.exec(content)) !== null) { | ||
| const objectStart = content.indexOf('{', propertyPattern.lastIndex); | ||
| if (objectStart === -1) throw new Error('Could not find a static properties object.'); | ||
|
|
||
| const objectEnd = findObjectEnd(content, objectStart); | ||
| const semicolon = content[objectEnd + 1] === ';' ? 1 : 0; | ||
| const declaration = content.slice(match.index, objectEnd + 1 + semicolon); | ||
| const object = content.slice(objectStart, objectEnd + 1); | ||
|
|
||
| result += content.slice(sourceIndex, match.index); | ||
| result += declaration.replace(/\bstatic\s+properties\s*=\s*/, 'static get properties() { return ') | ||
| .replace(object, `${object}; }`); | ||
| sourceIndex = objectEnd + 1 + semicolon; | ||
| propertyPattern.lastIndex = sourceIndex; | ||
| } | ||
|
|
||
| return result + content.slice(sourceIndex); | ||
| } | ||
|
|
||
| async function transformFiles(files, temporaryDirectory) { | ||
| for (const file of files) { | ||
| const sourcePath = resolve(file); | ||
| const relativePath = relative(process.cwd(), sourcePath); | ||
| const destinationPath = join(temporaryDirectory, relativePath); | ||
|
|
||
| await mkdir(dirname(destinationPath), { recursive: true }); | ||
| const content = await readFile(sourcePath, 'utf8'); | ||
| await writeFile(destinationPath, transformProperties(content)); | ||
| } | ||
| } | ||
|
|
||
| function restorePaths(value, temporaryPath) { | ||
| if (Array.isArray(value)) { | ||
| value.forEach(item => restorePaths(item, temporaryPath)); | ||
| } else if (value && typeof value === 'object') { | ||
| Object.entries(value).forEach(([key, item]) => { | ||
| if (key === 'path' && typeof item === 'string') { | ||
| value[key] = item.replace(temporaryPath, '.'); | ||
| } else { | ||
| restorePaths(item, temporaryPath); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| async function restoreCustomElementsPaths(temporaryDirectory) { | ||
| const customElements = JSON.parse(await readFile(customElementsFileName, 'utf8')); | ||
| const temporaryPath = `./${relative(process.cwd(), temporaryDirectory)}`; | ||
| restorePaths(customElements, temporaryPath); | ||
| await writeFile(customElementsFileName, `${JSON.stringify(customElements, null, 2)}\n`); | ||
| } | ||
|
|
||
| function runWca(src) { | ||
| return new Promise((resolve, reject) => { | ||
| const child = spawn('wca', [ | ||
| 'analyze', | ||
| src, | ||
| '--format', 'json', | ||
| '--outFile', customElementsFileName | ||
| ], { stdio: 'inherit' }); | ||
| child.once('error', reject); | ||
| child.once('close', (exitCode, signal) => { | ||
| if (exitCode === 0) { | ||
| resolve(); | ||
| } else { | ||
| reject(new Error(`wca exited with ${signal ? `signal ${signal}` : `code ${exitCode}`}`)); | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| async function main() { | ||
| const [glob] = process.argv.slice(2); | ||
| if (!glob) throw new Error('Usage: node ./cli/wca.js <glob>'); | ||
|
|
||
| const temporaryDirectory = await mkdtemp(join(tmpdir(), 'wca-')); | ||
|
|
||
| try { | ||
| const files = await Array.fromAsync(globFiles(glob, { nodir: true })); | ||
|
|
||
| await transformFiles(files, temporaryDirectory); | ||
|
|
||
| await runWca(join(temporaryDirectory, '**/*.js')); | ||
|
|
||
| await restoreCustomElementsPaths(temporaryDirectory); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've done a side-by-side diff of
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Committing
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be very noisy, and I think we'd need to build an action similar to the translation formatter / vdiff that would PR into your PR with the changes? Otherwise we're relying on devs running
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That could all live in the docs site instead, rather than committing it here. But yeah, it would probably require a dev review almost every day 😬. Can't decide if that's worth it
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I don't think we can fully move it to the docs site because |
||
| } finally { | ||
| await rm(temporaryDirectory, { recursive: true, force: true }); | ||
| } | ||
| } | ||
|
|
||
| main().catch(error => { | ||
| console.error(error); | ||
| process.exitCode = 1; | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,74 +2,72 @@ import { FocusMixin } from '../../mixins/focus/focus-mixin.js'; | |
|
|
||
| export const ButtonMixin = superclass => class extends FocusMixin(superclass) { | ||
|
|
||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had previously left these static getters in because our The good news is that we can now switch these to use the non-getter version. |
||
| static get properties() { | ||
| return { | ||
| /** | ||
| * @ignore | ||
| */ | ||
| ariaExpanded: { type: String, reflect: true, attribute: 'aria-expanded' }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| ariaHaspopup: { type: String, reflect: true, attribute: 'aria-haspopup' }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| ariaLabel: { type: String, reflect: true, attribute: 'aria-label' }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| // eslint-disable-next-line lit/no-native-attributes | ||
| autofocus: { type: Boolean, reflect: true }, | ||
| /** | ||
| * Wether the controlled element is expanded. Replaces 'aria-expanded' | ||
| * @type {'true' | 'false'} | ||
| */ | ||
| expanded: { type: String, reflect: true, attribute: 'expanded' }, | ||
| /** | ||
| * Disables the button | ||
| * @type {boolean} | ||
| */ | ||
| disabled: { type: Boolean, reflect: true }, | ||
| /** | ||
| * Tooltip text when disabled | ||
| * @type {string} | ||
| */ | ||
| disabledTooltip: { type: String, attribute: 'disabled-tooltip' }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| form: { type: String, reflect: true }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| formaction: { type: String, reflect: true }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| formenctype: { type: String, reflect: true }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| formmethod: { type: String, reflect: true }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| formnovalidate: { type: String, reflect: true }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| formtarget: { type: String, reflect: true }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| name: { type: String, reflect: true }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| type: { type: String, reflect: true } | ||
| }; | ||
| } | ||
| static properties = { | ||
| /** | ||
| * @ignore | ||
| */ | ||
| ariaExpanded: { type: String, reflect: true, attribute: 'aria-expanded' }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| ariaHaspopup: { type: String, reflect: true, attribute: 'aria-haspopup' }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| ariaLabel: { type: String, reflect: true, attribute: 'aria-label' }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| // eslint-disable-next-line lit/no-native-attributes | ||
| autofocus: { type: Boolean, reflect: true }, | ||
| /** | ||
| * Wether the controlled element is expanded. Replaces 'aria-expanded' | ||
| * @type {'true' | 'false'} | ||
| */ | ||
| expanded: { type: String, reflect: true, attribute: 'expanded' }, | ||
| /** | ||
| * Disables the button | ||
| * @type {boolean} | ||
| */ | ||
| disabled: { type: Boolean, reflect: true }, | ||
| /** | ||
| * Tooltip text when disabled | ||
| * @type {string} | ||
| */ | ||
| disabledTooltip: { type: String, attribute: 'disabled-tooltip' }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| form: { type: String, reflect: true }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| formaction: { type: String, reflect: true }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| formenctype: { type: String, reflect: true }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| formmethod: { type: String, reflect: true }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| formnovalidate: { type: String, reflect: true }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| formtarget: { type: String, reflect: true }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| name: { type: String, reflect: true }, | ||
| /** | ||
| * @ignore | ||
| */ | ||
| type: { type: String, reflect: true } | ||
| }; | ||
|
|
||
| constructor() { | ||
| super(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yay, a lexer!