Skip to content

Commit

Permalink
Merge pull request #550 from FabienArcellier/517-custom-component-nam…
Browse files Browse the repository at this point in the history
…e-should-be-constrained-explicitely

fix: custom component name should be constrained explicitely - WF-52
  • Loading branch information
ramedina86 authored Sep 20, 2024
2 parents 57a580e + 7f89714 commit 720ad31
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 8 deletions.
3 changes: 2 additions & 1 deletion alfred/npm.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

@alfred.command("npm.lint", help="lint check npm packages")
def npm_lint():
alfred.run("npm run ui:lint:ci")
alfred.run("npm run ui:lint.ci")
alfred.run("npm run ui:custom.check")

@alfred.command("npm.e2e", help="run e2e tests")
@alfred.option('--browser', '-b', help="run e2e tests on specified browser", default='chromium')
Expand Down
9 changes: 7 additions & 2 deletions docs/framework/custom-components.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,18 @@ A single or multiple templates can be specified. Take into account that they wil

## Bundling templates

Execute `npm run custom.build`, this will generate the output `.js` and `.css` files into `ui/custom_components_dist`.
Execute `npm run custom.build` into `src/ui`, this will generate the output `.js` and `.css` files into `./custom_components_dist`.

```sh
# "build" builds the entire front-end
# "custom.build" only builds the custom templates

npm run custom.check # Optional: checks certain issues on custom components
npm run custom.build
```

Collect the files from `ui/custom_components_dist` and pack them in a folder such as `my_custom_bubbles`. The folder containing the generated files, e.g. `my_custom_bubbles`, can now be placed in the `extensions/` folder of any Framework project. It'll be automatically detected during server startup.
Collect the files from `./custom_components_dist` and pack them in a folder such as `my_custom_bubbles`. The folder containing the generated files, e.g. `my_custom_bubbles`, can now be placed in the `extensions/` folder of any Framework project. It'll be automatically detected during server startup.

<Tip>
The `custom.check` command is optional, but it's recommended to run it before building the custom components. It checks for common issues in the custom components, such as invalid key declaration, ...
</Tip>
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
"ui:build": "npm run -w writer-ui build",
"ui:preview": "npm run -w writer-ui preview",
"ui:custom.build": "npm run -w writer-ui custom.build",
"ui:custom.check": "npm run -w writer-ui custom.check",
"ui:lint": "npm run -w writer-ui lint",
"ui:lint:ci": "npm run -w writer-ui lint:ci",
"ui:lint.ci": "npm run -w writer-ui lint.ci",

"docs:codegen": "npm run -w writer-docs codegen",
"docs:preview": "npm run -w writer-docs preview",
Expand Down
5 changes: 3 additions & 2 deletions src/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
"build": "vite build",
"preview": "vite preview --port 5050",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path ../../.gitignore --ignore-path .gitignore",
"lint:ci": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --ignore-path ../../.gitignore --ignore-path .gitignore",
"lint.ci": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --ignore-path ../../.gitignore --ignore-path .gitignore",
"codegen": "node tools/generator.mjs",
"custom.dev": "vite --config vite.config.custom.ts",
"custom.build": "vite build --config vite.config.custom.ts",
"custom.check": "node tools/custom_check.mjs",
"storybook": "storybook dev -p 6006",
"storybook.build": "storybook build"
},
Expand Down Expand Up @@ -61,4 +62,4 @@
"eslint-plugin-storybook": "0.8.0",
"storybook": "8.0.5"
}
}
}
27 changes: 25 additions & 2 deletions src/ui/src/core/loadExtensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,33 @@ async function importCustomComponentTemplate(path: string) {
await import(/* @vite-ignore */ getRelativeExtensionsPath() + path);
Object.entries(window[CUSTOM_COMPONENTS_GLOBAL_VAR])?.forEach(
([key, template]) => {
console.log(`Registering template for "${key}".`);
registerComponentTemplate(`custom_${key}`, template);
if (checkComponentKey(key)) {
registerComponentTemplate(`custom_${key}`, template);
console.log(`Registering template for "${key}".`);
} else {
console.warn(
`custom component '${key}' is ignored. A custom component should be declared using only alphanumeric lowercase and _.`,
);
}
},
);
}

/**
* Checks that the key contains only alphanumeric characters and underscores without capital letters
* The clipboard api use in drag and drop doesn't handle uppercase.
*
* mycomponent : valid
* myComponent : invalid
* myCOMPONENT : invalid
*
* @see https://github.com/writer/writer-framework/issues/517
*/
export function checkComponentKey(key: string): boolean {
const isValidKey = /^[a-z0-9_]+$/.test(key);
return isValidKey;
}

function loadStylesheet(path: string) {
const el: HTMLLinkElement = document.createElement("link");
el.rel = "stylesheet";
Expand All @@ -41,3 +62,5 @@ function getRelativeExtensionsPath() {

return `${pathname}extensions/`;
}


18 changes: 18 additions & 0 deletions src/ui/tools/core.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,21 @@ export async function loadComponents() {

return data;
}

/**
* imports a vue-dependent module.
*/
export async function importVue(modulePath) {
const vite = await createServer({
includeWriterComponentPath: true,
server: {
middlewareMode: true,
},
appType: "custom",
});

const m = await vite.ssrLoadModule(path.join(__dirname, modulePath));
await vite.close();

return m;
}
42 changes: 42 additions & 0 deletions src/ui/tools/custom_check.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { importVue } from "./core.mjs";

async function checkDeclarationKey() {
let hasFailed = false;
const module = await importVue("../src/custom_components/index.ts");
const { checkComponentKey } = await importVue(
"../src/core/loadExtensions.ts",
);
const invalidCustomComponentKeys = [];
Object.keys(module.default).forEach((key) => {
if (!checkComponentKey(key)) {
invalidCustomComponentKeys.push(key);
hasFailed = true;
}
});

if (invalidCustomComponentKeys.length !== 0) {
// eslint-disable-next-line no-console
console.error(
`ERROR: Invalid component declaration: ${invalidCustomComponentKeys} into 'src/custom_components/index.ts'. Their key must be declared using only lowercase and alphanumeric characters.`,
);
}
return hasFailed;
}

/**
* Check the custom components in continuous integration
*
* npm run custom.check
*
*/
async function check() {
let hasFailed = false;

hasFailed |= await checkDeclarationKey();

if (hasFailed) {
process.exit(1);
}
}

check();

0 comments on commit 720ad31

Please sign in to comment.