Compare commits
No commits in common. "cb8fbb4b3b009420e7c9b113466d9f0611ef2e6c" and "86c2d71b5efb7546badfe3ce3904d762f2661b45" have entirely different histories.
cb8fbb4b3b
...
86c2d71b5e
32 changed files with 1 additions and 3120 deletions
|
|
@ -1,8 +0,0 @@
|
||||||
# Changesets
|
|
||||||
|
|
||||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
|
||||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
|
||||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets).
|
|
||||||
|
|
||||||
We have a quick list of common questions to get you started engaging with this project in
|
|
||||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md).
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://unpkg.com/@changesets/config@3.1.4/schema.json",
|
|
||||||
"changelog": "@changesets/cli/changelog",
|
|
||||||
"commit": false,
|
|
||||||
"fixed": [],
|
|
||||||
"linked": [],
|
|
||||||
"access": "restricted",
|
|
||||||
"baseBranch": "main",
|
|
||||||
"updateInternalDependencies": "patch",
|
|
||||||
"ignore": []
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
---
|
|
||||||
"@lightweight-sdk/web-component": patch
|
|
||||||
---
|
|
||||||
|
|
||||||
Migration de `web-component` dans le monorepo lightweight-sdk. Aucun changement de comportement : le code source est repris tel quel depuis l'ancien repo `lightweight-sdk-web-component`, seuls l'outillage (Biome, Changesets, publication sur le registre npm privé) et l'organisation du dépôt changent.
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
indent_style = tab
|
|
||||||
indent_size = 2
|
|
||||||
end_of_line = lf
|
|
||||||
charset = utf-8
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
|
||||||
|
|
||||||
[*.{yml,yaml}]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
trim_trailing_whitespace = false
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-lint-test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: "24"
|
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@v4
|
|
||||||
with:
|
|
||||||
run_install: false
|
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-cache
|
|
||||||
shell: bash
|
|
||||||
run: echo "STORE_PATH=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Cache pnpm store
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
- name: Lint (Biome)
|
|
||||||
run: pnpm lint
|
|
||||||
|
|
||||||
- name: Build (tsc -b)
|
|
||||||
run: pnpm build
|
|
||||||
|
|
||||||
- name: Test (Vitest)
|
|
||||||
run: pnpm test
|
|
||||||
|
|
||||||
- name: Docs check (TypeDoc validation)
|
|
||||||
run: pnpm docs:check
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
# Publie les packages modifiés sur le registre npm privé Forgejo
|
|
||||||
# (https://git.dev.kyuno.fr/api/packages/kyuno/npm/) via Changesets.
|
|
||||||
#
|
|
||||||
# - déclenché sur push vers `main` (typiquement après le merge d'une PR
|
|
||||||
# "Version Packages" générée par `pnpm changeset version`)
|
|
||||||
# - `changeset publish` est idempotent par package : republie uniquement
|
|
||||||
# les packages dont la version locale n'existe pas encore sur le registre
|
|
||||||
# - prérequis côté instance : secret de repo REGISTRY_TOKEN, un jeton
|
|
||||||
# Forgejo avec les scopes write:package + read:package (Settings ->
|
|
||||||
# Applications) - le jeton automatique des Actions n'a pas ce droit
|
|
||||||
# sur cette instance (voir spec/ci-cd.md)
|
|
||||||
|
|
||||||
name: Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: "24"
|
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@v4
|
|
||||||
with:
|
|
||||||
run_install: false
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: pnpm build
|
|
||||||
|
|
||||||
- name: Configure registry authentication
|
|
||||||
run: echo "//git.dev.kyuno.fr/api/packages/kyuno/npm/:_authToken=${REGISTRY_TOKEN}" >> .npmrc
|
|
||||||
env:
|
|
||||||
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
||||||
|
|
||||||
- name: Publish changed packages
|
|
||||||
run: pnpm changeset:publish
|
|
||||||
env:
|
|
||||||
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
||||||
1
.gitattributes
vendored
1
.gitattributes
vendored
|
|
@ -1 +0,0 @@
|
||||||
* text=auto eol=lf
|
|
||||||
15
.gitignore
vendored
15
.gitignore
vendored
|
|
@ -1,15 +0,0 @@
|
||||||
node_modules/
|
|
||||||
dist/
|
|
||||||
*.tsbuildinfo
|
|
||||||
docs/api/
|
|
||||||
coverage/
|
|
||||||
*.log
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
.vscode/
|
|
||||||
.env
|
|
||||||
.env.*.local
|
|
||||||
|
|
||||||
# Claude Code (règle CLAUDE.md : ne jamais commit ceci)
|
|
||||||
.claude/
|
|
||||||
CLAUDE.md
|
|
||||||
1
.npmrc
1
.npmrc
|
|
@ -1 +0,0 @@
|
||||||
@lightweight-sdk:registry=https://git.dev.kyuno.fr/api/packages/kyuno/npm/
|
|
||||||
35
README.md
35
README.md
|
|
@ -1,36 +1,3 @@
|
||||||
# lightweight-sdk
|
# lightweight-sdk
|
||||||
|
|
||||||
Bibliothèque personnelle de packages TypeScript, organisée en monorepo pnpm et publiée sur le registre npm privé hébergé sur [git.dev.kyuno.fr](https://git.dev.kyuno.fr/kyuno).
|
Bibliothèque personnelle de packages TypeScript (monorepo pnpm), publiée sur le registre npm privé Forgejo.
|
||||||
|
|
||||||
## Structure
|
|
||||||
|
|
||||||
Les packages sont répartis en trois catégories sous `packages/` :
|
|
||||||
|
|
||||||
- `client/` — packages destinés au navigateur (DOM, composants UI).
|
|
||||||
- `common/` — code isomorphe partagé entre client et server.
|
|
||||||
- `server/` — packages Node/backend, structurés en services modulaires autonomes.
|
|
||||||
|
|
||||||
Voir [`spec/architecture.md`](./spec/architecture.md) pour le détail des choix d'outillage, et [`spec/packages.md`](./spec/packages.md) pour la liste des packages existants.
|
|
||||||
|
|
||||||
## Démarrage
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm install
|
|
||||||
pnpm build
|
|
||||||
pnpm lint
|
|
||||||
pnpm test
|
|
||||||
pnpm docs:check
|
|
||||||
```
|
|
||||||
|
|
||||||
## Créer un nouveau package
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm create-package <client|common|server> <nom-du-package>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
- [`spec/architecture.md`](./spec/architecture.md) — vue d'ensemble et choix techniques.
|
|
||||||
- [`spec/conventions.md`](./spec/conventions.md) — conventions de code et traçabilité avec les directives du projet.
|
|
||||||
- [`spec/ci-cd.md`](./spec/ci-cd.md) — CI, versioning (Changesets) et publication sur le registre.
|
|
||||||
- [`spec/packages.md`](./spec/packages.md) — registre des packages du monorepo.
|
|
||||||
62
biome.json
62
biome.json
|
|
@ -1,62 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://biomejs.dev/schemas/2.5.14/schema.json",
|
|
||||||
"vcs": {
|
|
||||||
"enabled": true,
|
|
||||||
"clientKind": "git",
|
|
||||||
"useIgnoreFile": true
|
|
||||||
},
|
|
||||||
"files": {
|
|
||||||
"ignoreUnknown": false
|
|
||||||
},
|
|
||||||
"formatter": {
|
|
||||||
"enabled": true,
|
|
||||||
"indentStyle": "tab",
|
|
||||||
"indentWidth": 2,
|
|
||||||
"lineWidth": 100
|
|
||||||
},
|
|
||||||
"linter": {
|
|
||||||
"enabled": true,
|
|
||||||
"rules": {
|
|
||||||
"preset": "recommended",
|
|
||||||
"style": {
|
|
||||||
"useConsistentMemberAccessibility": {
|
|
||||||
"level": "error",
|
|
||||||
"options": { "accessibility": "explicit" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nursery": {
|
|
||||||
"noFloatingPromises": "error",
|
|
||||||
"noMisusedPromises": "error",
|
|
||||||
"useAwaitThenable": "error"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"javascript": {
|
|
||||||
"formatter": {
|
|
||||||
"quoteStyle": "single",
|
|
||||||
"semicolons": "always",
|
|
||||||
"trailingCommas": "all"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"assist": {
|
|
||||||
"enabled": true,
|
|
||||||
"actions": {
|
|
||||||
"source": {
|
|
||||||
"organizeImports": "on"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"includes": ["packages/client/web-component/src/**"],
|
|
||||||
"formatter": { "enabled": false },
|
|
||||||
"linter": {
|
|
||||||
"rules": {
|
|
||||||
"suspicious": {
|
|
||||||
"noShadowRestrictedNames": "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
34
package.json
34
package.json
|
|
@ -1,34 +0,0 @@
|
||||||
{
|
|
||||||
"name": "lightweight-sdk",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"private": true,
|
|
||||||
"packageManager": "pnpm@12.4.2",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=22"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"build": "tsc -b",
|
|
||||||
"build:clean": "tsc -b --clean",
|
|
||||||
"dev": "tsc -b --watch",
|
|
||||||
"lint": "biome check .",
|
|
||||||
"lint:fix": "biome check --write .",
|
|
||||||
"format": "biome format --write .",
|
|
||||||
"test": "vitest run",
|
|
||||||
"test:watch": "vitest",
|
|
||||||
"docs:check": "typedoc --options typedoc.json --emit none --treatWarningsAsErrors --disableGit",
|
|
||||||
"docs:build": "typedoc --options typedoc.json",
|
|
||||||
"changeset": "changeset",
|
|
||||||
"changeset:version": "changeset version",
|
|
||||||
"changeset:publish": "changeset publish",
|
|
||||||
"create-package": "node ./scripts/create-package.mjs",
|
|
||||||
"ci": "pnpm lint && pnpm build && pnpm test && pnpm docs:check"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@biomejs/biome": "catalog:",
|
|
||||||
"@changesets/cli": "catalog:",
|
|
||||||
"@types/node": "catalog:",
|
|
||||||
"typedoc": "catalog:",
|
|
||||||
"typescript": "catalog:",
|
|
||||||
"vitest": "catalog:"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
# lightweight-sdk-web-component
|
|
||||||
|
|
||||||
A lightweight library to build web components easily.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm add @lightweight-sdk/web-component
|
|
||||||
```
|
|
||||||
|
|
||||||
## Prérequis côté consommateur
|
|
||||||
|
|
||||||
`@LinkToDom` et `@CustomComponent` sont des décorateurs TypeScript "legacy" (signature `(target, key)` / `(constructor)`). Pour les utiliser, ton propre `tsconfig.json` doit activer :
|
|
||||||
|
|
||||||
```jsonc
|
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"experimentalDecorators": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { AbstractComponent, CustomComponent, LinkToDom } from "@lightweight-sdk/web-component";
|
|
||||||
|
|
||||||
@CustomComponent({
|
|
||||||
tagName: "my-widget",
|
|
||||||
template: "<span ref=\"label\"></span>",
|
|
||||||
})
|
|
||||||
class MyWidget extends AbstractComponent {
|
|
||||||
@LinkToDom("label")
|
|
||||||
private label!: HTMLElement;
|
|
||||||
|
|
||||||
public override init(): void {
|
|
||||||
this.label.textContent = "Hello";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
- `AbstractComponent` — classe de base à étendre pour déclarer un web component (cycle de vie `init`/`refresh`).
|
|
||||||
- `CustomComponent(params)` — décorateur de classe qui enregistre le custom element et branche le template/Shadow DOM.
|
|
||||||
- `LinkToDom(ref)` — décorateur de propriété qui lie un membre de la classe à l'élément DOM portant l'attribut `ref="..."` correspondant.
|
|
||||||
- `findRefElement(ref, parent?)` — retrouve un élément par son attribut `ref` dans un conteneur donné (`document.body` par défaut).
|
|
||||||
- `ComponentParams`, `LinkedProperty`, `AcceptedHTMLContent` — types associés.
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@lightweight-sdk/web-component",
|
|
||||||
"version": "0.0.2",
|
|
||||||
"description": "A lightweight library to build web components easily",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"types": "dist/index.d.ts",
|
|
||||||
"files": [
|
|
||||||
"dist"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=22"
|
|
||||||
},
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "restricted",
|
|
||||||
"registry": "https://git.dev.kyuno.fr/api/packages/kyuno/npm/"
|
|
||||||
},
|
|
||||||
"typedocOptions": {
|
|
||||||
"entryPoints": [
|
|
||||||
"src/index.ts"
|
|
||||||
],
|
|
||||||
"readme": "README.md",
|
|
||||||
"validation": {
|
|
||||||
"notExported": false,
|
|
||||||
"invalidLink": false,
|
|
||||||
"notDocumented": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
import { ComponentParams, CUSTOM_COMPONENT_CONFIG, LinkedProperty } from "./component.type";
|
|
||||||
|
|
||||||
/** Basic class used to declare web components */
|
|
||||||
export class AbstractComponent extends HTMLElement {
|
|
||||||
|
|
||||||
/** List of all the class members linked to a dom element */
|
|
||||||
public __linkedProperties?: LinkedProperty[]
|
|
||||||
/** All the component's params */
|
|
||||||
public [CUSTOM_COMPONENT_CONFIG]?: ComponentParams;
|
|
||||||
|
|
||||||
public connectedCallback(): void {
|
|
||||||
this.init();
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
public disconnectedCallback(): void { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function called in the component's life cycle
|
|
||||||
* Used to init the DOM and the sub components
|
|
||||||
*/
|
|
||||||
public init(): void { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function called in the component's life cycle
|
|
||||||
* Used to refresh the datas, and re render the component
|
|
||||||
* Please be conscious that if you clean THIS as a dom element, the template won't be re rendered again
|
|
||||||
*/
|
|
||||||
public refresh(): void { }
|
|
||||||
}
|
|
||||||
|
|
@ -1,110 +0,0 @@
|
||||||
import { AbstractComponent } from "./abstractComponent";
|
|
||||||
import { ComponentParams, CUSTOM_COMPONENT_CONFIG } from "./component.type";
|
|
||||||
import { findRefElement, parseModuleToHtml } from "./dom.tools";
|
|
||||||
|
|
||||||
// Declare a unique key to store the flag on the prototype.
|
|
||||||
// This symbol is used to uniquely identify and access the DOM link flag,
|
|
||||||
// ensuring there are no conflicts with other properties on the prototype.
|
|
||||||
export const REQUEST_DOM_LINK_KEY = Symbol("requestDomLink");
|
|
||||||
|
|
||||||
interface ConstructorFunction<T> {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
new(...args: any[]): T
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a custom web component using a decorator.
|
|
||||||
* The decorator configures the component's template, styles, and any additional
|
|
||||||
* properties, as well as handling Shadow DOM attachment if specified.
|
|
||||||
* @param p @see {@link ComponentParams}
|
|
||||||
* @returns a constructor factory
|
|
||||||
*/
|
|
||||||
export function CustomComponent<T extends ConstructorFunction<AbstractComponent>>(p: ComponentParams): (constructor: T) => void {
|
|
||||||
return (constructor: T): void => {
|
|
||||||
|
|
||||||
// Create a template element to contain the component's HTML structure, parsed for use.
|
|
||||||
const templateElement = document.createElement('template');
|
|
||||||
templateElement.innerHTML = parseModuleToHtml(p.template);
|
|
||||||
|
|
||||||
// Attach component configuration as a non-writable property on the prototype
|
|
||||||
Object.defineProperty(constructor.prototype, CUSTOM_COMPONENT_CONFIG, {
|
|
||||||
value: p
|
|
||||||
});
|
|
||||||
|
|
||||||
// Save the original connectedCallback, if it exists, to allow for extension.
|
|
||||||
const originalConnectedCallback = constructor.prototype.connectedCallback;
|
|
||||||
|
|
||||||
// Define a custom connectedCallback for the component
|
|
||||||
constructor.prototype.connectedCallback = function (this: AbstractComponent) {
|
|
||||||
|
|
||||||
// If Shadow DOM is specified, attach it and add the template to it
|
|
||||||
if (p.attachToShadow) {
|
|
||||||
// Determine Shadow DOM mode, defaulting to 'open' if boolean true is given.
|
|
||||||
const mode: ShadowRootMode = typeof p.attachToShadow === "boolean" ? "open" : p.attachToShadow;
|
|
||||||
|
|
||||||
// Attach a Shadow DOM if not already attached
|
|
||||||
if (!this.shadowRoot) {
|
|
||||||
this.attachShadow({ mode });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append the template content to the Shadow DOM
|
|
||||||
if (this.shadowRoot) {
|
|
||||||
this.shadowRoot.appendChild(templateElement.content.cloneNode(true));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If not using Shadow DOM, insert template content directly into Light DOM
|
|
||||||
this.innerHTML = "";
|
|
||||||
this.appendChild(templateElement.content.cloneNode(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply any specified CSS classes to the component root element
|
|
||||||
if (p.cssClasses) {
|
|
||||||
this.classList.add(...p.cssClasses);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process and link properties specified in __linkedProperties to their DOM elements
|
|
||||||
for (const requested of this.__linkedProperties ?? []) {
|
|
||||||
const elt = findRefElement(requested.ref, this);
|
|
||||||
if (elt == null) {
|
|
||||||
throw new Error(`ref ${requested.ref} not found`);
|
|
||||||
}
|
|
||||||
// Attach the found element as a property on the component instance
|
|
||||||
Object.defineProperty(this, requested.property, { value: elt });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call the original connectedCallback if it exists
|
|
||||||
if (originalConnectedCallback) {
|
|
||||||
originalConnectedCallback.call(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// Register the custom element with the specified tag name
|
|
||||||
window.customElements.define(p.tagName, constructor);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decorator that flags the class member as a link to a dom element
|
|
||||||
* @param ref
|
|
||||||
*/
|
|
||||||
export function LinkToDom(ref: string) {
|
|
||||||
return function <T extends AbstractComponent>(target: T, key: string | symbol): void {
|
|
||||||
// Si target est indéfini, renvoyer un avertissement
|
|
||||||
if (!target) {
|
|
||||||
throw new Error(`LinkToDom: 'target' is undefined for property ${String(key)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ajouter un flag `request` pour marquer cette propriété comme liée au DOM
|
|
||||||
if (!target.__linkedProperties) {
|
|
||||||
target.__linkedProperties = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ajouter la propriété à la liste des propriétés liées
|
|
||||||
target.__linkedProperties.push({
|
|
||||||
property: String(key),
|
|
||||||
ref
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
|
|
||||||
export const CUSTOM_COMPONENT_CONFIG = "__customComponentConfig"
|
|
||||||
|
|
||||||
/** Abstract component params*/
|
|
||||||
export interface ComponentParams {
|
|
||||||
/**
|
|
||||||
* A tag name that will be used to register the component as a custom element
|
|
||||||
* Should always follow the following format "my-name"
|
|
||||||
* ex: "custom-menu-component"
|
|
||||||
*/
|
|
||||||
tagName: string;
|
|
||||||
/** HTML Template */
|
|
||||||
template?: string;
|
|
||||||
/** Specific style of the component */
|
|
||||||
style?: string;
|
|
||||||
/** Css classes to append */
|
|
||||||
cssClasses?: string[];
|
|
||||||
/** Define if the component should be atatched to the shadow dom */
|
|
||||||
attachToShadow?: boolean | ShadowRootMode
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Map a property key to a html element by it's ref attribute */
|
|
||||||
export interface LinkedProperty {
|
|
||||||
/** The ref attribute value */
|
|
||||||
ref: string;
|
|
||||||
/** The object's property */
|
|
||||||
property: string;
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
|
|
||||||
/** The attriute used to bind dom to component's members */
|
|
||||||
const REF_ATTRIBUTE = "ref"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find an element with the attribute ref= in the given parent (or the entire body)
|
|
||||||
* @param ref the ref attribute's value of the target dom element
|
|
||||||
* @param parent the container used to search the element. document.body by default
|
|
||||||
* @returns the founded html element or undefined
|
|
||||||
*/
|
|
||||||
export function findRefElement(ref: string, parent?: HTMLElement): HTMLElement | undefined {
|
|
||||||
const parentElement = parent ?? document.body;
|
|
||||||
const elt = parentElement.querySelector(`[${REF_ATTRIBUTE}=${ref}]`);
|
|
||||||
|
|
||||||
return elt == null ? undefined : elt as HTMLElement
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse the value of ComponentParams.template into a string if it was fetched with required function
|
|
||||||
* @param mod the template value
|
|
||||||
* @returns the template as a string
|
|
||||||
*/
|
|
||||||
export function parseModuleToHtml(mod: any): string {
|
|
||||||
if (mod == null) return "";
|
|
||||||
return "default" in mod ? mod.default as string : "";
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
/** Anything that could be append in html*/
|
|
||||||
export type AcceptedHTMLContent = HTMLElement | string
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
export { AbstractComponent } from "./abstractComponent";
|
|
||||||
export { CustomComponent, LinkToDom } from "./component.tools";
|
|
||||||
export { ComponentParams, LinkedProperty } from "./component.type";
|
|
||||||
export { findRefElement } from "./dom.tools";
|
|
||||||
export { AcceptedHTMLContent } from "./dom.types";
|
|
||||||
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "../../../tsconfig.base.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
||||||
"outDir": "./dist",
|
|
||||||
"rootDir": "./src"
|
|
||||||
},
|
|
||||||
"include": ["src/**/*"]
|
|
||||||
}
|
|
||||||
2145
pnpm-lock.yaml
2145
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
|
@ -1,18 +0,0 @@
|
||||||
packages:
|
|
||||||
- "packages/*/*"
|
|
||||||
|
|
||||||
allowBuilds:
|
|
||||||
esbuild: true
|
|
||||||
|
|
||||||
# human-id@4.1+ est publié en ESM pur ; @changesets/write le require() en CJS
|
|
||||||
# et casse (ERR_REQUIRE_ESM). 4.0.0 est la dernière version CommonJS.
|
|
||||||
overrides:
|
|
||||||
human-id: "4.0.0"
|
|
||||||
|
|
||||||
catalog:
|
|
||||||
typescript: ^5.7.0
|
|
||||||
"@types/node": ^22.10.0
|
|
||||||
vitest: ^2.1.0
|
|
||||||
"@biomejs/biome": ^2.4.0
|
|
||||||
typedoc: ^0.28.0
|
|
||||||
"@changesets/cli": ^2.27.0
|
|
||||||
|
|
@ -1,153 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
import { execFileSync } from 'node:child_process';
|
|
||||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
||||||
import { dirname, join } from 'node:path';
|
|
||||||
import { fileURLToPath } from 'node:url';
|
|
||||||
|
|
||||||
const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
||||||
const CATEGORIES = ['client', 'common', 'server'];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints usage information and exits with an error status.
|
|
||||||
* @param {string} message the reason the script is aborting
|
|
||||||
*/
|
|
||||||
function fail(message) {
|
|
||||||
console.error(`Erreur : ${message}`);
|
|
||||||
console.error('Usage : pnpm create-package <client|common|server> <nom-du-package>');
|
|
||||||
process.exitCode = 1;
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const [, , category, name] = process.argv;
|
|
||||||
|
|
||||||
if (!category || !CATEGORIES.includes(category)) {
|
|
||||||
fail(`catégorie invalide (attendu : ${CATEGORIES.join(', ')})`);
|
|
||||||
}
|
|
||||||
if (!name || !/^[a-z][a-z0-9-]*[a-z0-9]$/.test(name)) {
|
|
||||||
fail('nom de package invalide (attendu : kebab-case, ex. "my-widget")');
|
|
||||||
}
|
|
||||||
|
|
||||||
const packageDir = join(ROOT, 'packages', category, name);
|
|
||||||
if (existsSync(packageDir)) {
|
|
||||||
fail(`packages/${category}/${name} existe déjà`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const npmName = `@lightweight-sdk/${name}`;
|
|
||||||
const isClient = category === 'client';
|
|
||||||
|
|
||||||
mkdirSync(join(packageDir, 'src'), { recursive: true });
|
|
||||||
|
|
||||||
const packageJson = {
|
|
||||||
name: npmName,
|
|
||||||
version: '0.0.1',
|
|
||||||
description: `TODO: décrire le package ${name}.`,
|
|
||||||
main: 'dist/index.js',
|
|
||||||
types: 'dist/index.d.ts',
|
|
||||||
files: ['dist'],
|
|
||||||
engines: { node: '>=22' },
|
|
||||||
publishConfig: {
|
|
||||||
access: 'restricted',
|
|
||||||
registry: 'https://git.dev.kyuno.fr/api/packages/kyuno/npm/',
|
|
||||||
},
|
|
||||||
typedocOptions: {
|
|
||||||
entryPoints: ['src/index.ts'],
|
|
||||||
readme: 'README.md',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
writeFileSync(join(packageDir, 'package.json'), `${JSON.stringify(packageJson, null, '\t')}\n`);
|
|
||||||
|
|
||||||
const tsconfig = {
|
|
||||||
extends: '../../../tsconfig.base.json',
|
|
||||||
compilerOptions: isClient
|
|
||||||
? { lib: ['ES2022', 'DOM', 'DOM.Iterable'], outDir: './dist', rootDir: './src' }
|
|
||||||
: { outDir: './dist', rootDir: './src' },
|
|
||||||
include: ['src/**/*'],
|
|
||||||
};
|
|
||||||
writeFileSync(join(packageDir, 'tsconfig.json'), `${JSON.stringify(tsconfig, null, '\t')}\n`);
|
|
||||||
|
|
||||||
const indexTs = `/**
|
|
||||||
* Placeholder function for the ${npmName} package — replace with real exports.
|
|
||||||
* @param subject the name to greet
|
|
||||||
* @returns a greeting for the given subject
|
|
||||||
*/
|
|
||||||
export function greet(subject: string): string {
|
|
||||||
return \`Hello, \${subject}!\`;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
writeFileSync(join(packageDir, 'src', 'index.ts'), indexTs);
|
|
||||||
|
|
||||||
const indexTest = `import { describe, expect, it } from 'vitest';
|
|
||||||
import { greet } from './index';
|
|
||||||
|
|
||||||
describe('greet', () => {
|
|
||||||
it('greets the given subject', () => {
|
|
||||||
expect(greet('world')).toBe('Hello, world!');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
`;
|
|
||||||
writeFileSync(join(packageDir, 'src', 'index.test.ts'), indexTest);
|
|
||||||
|
|
||||||
const commonNote =
|
|
||||||
category === 'common'
|
|
||||||
? "\n> `common/` doit rester isomorphe : pas d'API exclusivement DOM ou exclusivement Node.\n"
|
|
||||||
: '';
|
|
||||||
const readme = `# ${npmName}
|
|
||||||
${commonNote}
|
|
||||||
TODO: décrire le package.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
\`\`\`bash
|
|
||||||
pnpm add ${npmName}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
TODO
|
|
||||||
`;
|
|
||||||
writeFileSync(join(packageDir, 'README.md'), readme);
|
|
||||||
|
|
||||||
// Édite le tsconfig.json racine (project references)
|
|
||||||
const rootTsconfigPath = join(ROOT, 'tsconfig.json');
|
|
||||||
const rootTsconfig = JSON.parse(readFileSync(rootTsconfigPath, 'utf8'));
|
|
||||||
rootTsconfig.references ??= [];
|
|
||||||
rootTsconfig.references.push({ path: `./packages/${category}/${name}` });
|
|
||||||
writeFileSync(rootTsconfigPath, `${JSON.stringify(rootTsconfig, null, '\t')}\n`);
|
|
||||||
|
|
||||||
// Ajoute une ligne au registre des packages
|
|
||||||
const packagesSpecPath = join(ROOT, 'spec', 'packages.md');
|
|
||||||
const packagesSpec = readFileSync(packagesSpecPath, 'utf8');
|
|
||||||
const newRow = `| \`${npmName}\` | ${category} | 0.0.1 | brouillon |\n`;
|
|
||||||
writeFileSync(packagesSpecPath, packagesSpec.trimEnd() + '\n' + newRow);
|
|
||||||
|
|
||||||
console.log(`Package ${npmName} créé dans packages/${category}/${name}/.`);
|
|
||||||
|
|
||||||
try {
|
|
||||||
execFileSync('pnpm', ['install'], { cwd: ROOT, stdio: 'inherit' });
|
|
||||||
execFileSync(
|
|
||||||
'pnpm',
|
|
||||||
[
|
|
||||||
'exec',
|
|
||||||
'biome',
|
|
||||||
'check',
|
|
||||||
'--write',
|
|
||||||
`packages/${category}/${name}`,
|
|
||||||
'tsconfig.json',
|
|
||||||
'spec/packages.md',
|
|
||||||
],
|
|
||||||
{ cwd: ROOT, stdio: 'inherit' },
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Étape automatique échouée, à relancer toi-même :');
|
|
||||||
console.error(' pnpm install');
|
|
||||||
console.error(
|
|
||||||
` pnpm exec biome check --write packages/${category}/${name} tsconfig.json spec/packages.md`,
|
|
||||||
);
|
|
||||||
console.error(String(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('\nProchaines étapes :');
|
|
||||||
console.log(` cd packages/${category}/${name}`);
|
|
||||||
console.log(' - compléter la description dans package.json et README.md');
|
|
||||||
console.log(` - pnpm --filter ${npmName} test`);
|
|
||||||
console.log(' - pnpm changeset (une fois prêt à publier)');
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
# Architecture
|
|
||||||
|
|
||||||
## Objectif
|
|
||||||
|
|
||||||
`lightweight-sdk` est un monorepo pnpm destiné à héberger, au fil du temps, des packages TypeScript réutilisables développés par l'auteur pour ses projets personnels. Chaque package est publié indépendamment sur le registre npm privé hébergé sur l'instance Forgejo personnelle (`git.dev.kyuno.fr`), sous le scope `@lightweight-sdk`.
|
|
||||||
|
|
||||||
## Organisation des packages
|
|
||||||
|
|
||||||
Les packages sont répartis en trois catégories sous `packages/`, qui reflètent la distinction Frontend/Backend du [CLAUDE.md](../CLAUDE.md) du projet :
|
|
||||||
|
|
||||||
- **`client/`** — packages destinés au navigateur (DOM, composants UI). Peuvent dépendre de `lib: DOM` dans leur `tsconfig.json`.
|
|
||||||
- **`common/`** — code isomorphe partagé entre `client` et `server` (types, utilitaires purs, constantes). Convention : ne doit dépendre d'aucune API exclusivement DOM ou exclusivement Node, pour rester portable des deux côtés.
|
|
||||||
- **`server/`** — packages Node/backend. Chaque package doit être structuré comme un service modulaire autonome exposant sa propre API (règle "Architecture Backend" du CLAUDE.md).
|
|
||||||
|
|
||||||
Le nom npm d'un package ne reprend pas le préfixe de catégorie (`@lightweight-sdk/web-component`, pas `@lightweight-sdk/client-web-component`) — la catégorie est une convention d'organisation du dépôt, pas une convention de nommage public.
|
|
||||||
|
|
||||||
Un nouveau package se crée avec `pnpm create-package <client|common|server> <nom>` (voir `scripts/create-package.mjs`).
|
|
||||||
|
|
||||||
## Choix d'outillage
|
|
||||||
|
|
||||||
| Aspect | Choix | Pourquoi |
|
|
||||||
|---|---|---|
|
|
||||||
| Gestionnaire de packages | pnpm workspaces + `catalog:` | Centralise les versions des devDependencies partagées dans `pnpm-workspace.yaml` |
|
|
||||||
| Orchestrateur de build | Aucun (pas de Turborepo/Nx) | Un seul package réel au démarrage ; à reconsidérer si le temps de build devient un problème |
|
|
||||||
| Build | `tsc -b` (project references), pas de bundler | Les packages sont consommés via pnpm par d'autres projets Node/TS de l'auteur ; pas besoin de bundle |
|
|
||||||
| Modules | **CommonJS**, résolution `Node10` | Décision explicite : pas d'extension `.js` requise dans les imports relatifs, et le code migré depuis l'ancien repo `web-component` compile sans modification |
|
|
||||||
| Lint + format | [Biome.js](https://biomejs.dev/) | Imposé par le CLAUDE.md du projet |
|
|
||||||
| Documentation de code | [TypeDoc](https://typedoc.org/) en mode validation | Génère la doc API et fait échouer la CI si un export n'a pas de TSDoc (voir `conventions.md`) |
|
|
||||||
| Tests | [Vitest](https://vitest.dev/) | ESM natif, bon support monorepo |
|
|
||||||
| Versioning/publish | [Changesets](https://github.com/changesets/changesets) | Gère des versions indépendantes par package + changelogs, comprend nativement `workspace:*` |
|
|
||||||
| Node cible | 24 (Active LTS) en CI, `engines: ">=22"` | Node 20 est end-of-life depuis avril 2026 |
|
|
||||||
|
|
||||||
## Dépendances entre packages
|
|
||||||
|
|
||||||
Les dépendances internes entre packages du monorepo utilisent le protocole `workspace:*` (résolu localement par pnpm, jamais via le registre distant). Le registre npm privé n'est utilisé que pour la publication et pour la consommation depuis des projets *externes* à ce monorepo.
|
|
||||||
|
|
||||||
## Registre npm privé
|
|
||||||
|
|
||||||
`https://git.dev.kyuno.fr/api/packages/kyuno/npm/` — voir `ci-cd.md` pour le détail de la publication et de l'authentification.
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
# CI/CD
|
|
||||||
|
|
||||||
## Workflows
|
|
||||||
|
|
||||||
Les workflows vivent dans `.forgejo/workflows/` (pas `.github/workflows/` — ce dernier n'est probablement pas surveillé par Forgejo Actions sur cette instance).
|
|
||||||
|
|
||||||
### `ci.yml`
|
|
||||||
|
|
||||||
Déclenché uniquement sur `pull_request` vers `main` (pas de trigger `push` séparé sur la branche source : il ferait tourner la même vérification deux fois sur le même commit dès qu'une PR est ouverte — deux statuts distincts pour un seul résultat logique, et une exposition doublée aux flakys du runner). Étapes : install (`--frozen-lockfile`) → `pnpm lint` → `pnpm build` → `pnpm test` → `pnpm docs:check`. Ne nécessite aucun secret (les dépendances internes entre packages passent par `workspace:*`, résolu localement).
|
|
||||||
|
|
||||||
### `release.yml`
|
|
||||||
|
|
||||||
Déclenché sur `push` vers `main` (typiquement après le merge d'une PR "Version Packages"). Étapes : install → build → ajout dynamique d'une ligne d'authentification dans `.npmrc` (jamais committée) → `pnpm changeset:publish`.
|
|
||||||
|
|
||||||
`changeset publish` est idempotent par package : il republie uniquement les packages dont la version locale n'existe pas encore sur le registre. Un push sur `main` sans bump de version ne publie donc rien.
|
|
||||||
|
|
||||||
## Flux Changesets (versioning + publication)
|
|
||||||
|
|
||||||
1. Pendant une PR de feature, ajouter un changeset : `pnpm changeset` (choisir le(s) package(s) concerné(s), le type de bump, décrire le changement). Le fichier généré dans `.changeset/` est commité avec la PR.
|
|
||||||
2. Quand on est prêt à publier, en local : `pnpm changeset version` — bump les `package.json` concernés et génère/met à jour leurs `CHANGELOG.md`.
|
|
||||||
3. Ouvrir une PR "Version Packages" avec ce résultat, la relire, la merger soi-même (jamais d'auto-merge).
|
|
||||||
4. Le merge sur `main` déclenche `release.yml`, qui publie effectivement sur le registre les packages dont la version a changé.
|
|
||||||
|
|
||||||
Pas de bot automatique type `changesets/action` (spécifique GitHub, incertain sur Forgejo) : le flux est volontairement manuel aux étapes 1-3.
|
|
||||||
|
|
||||||
## Registre npm privé Forgejo
|
|
||||||
|
|
||||||
- URL : `https://git.dev.kyuno.fr/api/packages/kyuno/npm/` (owner Forgejo = `kyuno`, indépendant du scope npm `@lightweight-sdk` — les deux ne sont pas liés).
|
|
||||||
- `.npmrc` (racine, committé) : `@lightweight-sdk:registry=https://git.dev.kyuno.fr/api/packages/kyuno/npm/`.
|
|
||||||
- Authentification : `//git.dev.kyuno.fr/api/packages/kyuno/npm/:_authToken=<token>` — jamais committé, injecté uniquement en CI via le secret `REGISTRY_TOKEN`.
|
|
||||||
- Limite connue : impossible de republier un nom+version déjà existant sur le registre (Changesets gère ça nativement en forçant un bump à chaque publication).
|
|
||||||
|
|
||||||
### Secret `REGISTRY_TOKEN`
|
|
||||||
|
|
||||||
1. Forgejo → Settings → Applications → générer un token avec les scopes `write:package` + `read:package`.
|
|
||||||
2. L'ajouter comme secret de repo nommé `REGISTRY_TOKEN` sur **ce** repo (`lightweight-sdk`) — les secrets de repo ne sont pas partagés entre repos sur cette instance ; celui de `watchparty` ne peut pas être réutilisé tel quel.
|
|
||||||
|
|
||||||
### Troubleshooting : 404 sur un package scopé
|
|
||||||
|
|
||||||
Si `pnpm changeset:publish` échoue avec une 404 sur un nom de package scopé (`@lightweight-sdk/...`), la cause probable est le reverse proxy devant l'instance : npm encode le `/` d'un nom scopé en `%2F`, ce qu'Apache refuse par défaut. Vérifier `AllowEncodedSlashes NoDecode` dans le VirtualHost et `nocanon` sur la directive `ProxyPass`. Ce point n'a pas pu être vérifié depuis l'agent (config réseau propre à l'instance).
|
|
||||||
|
|
||||||
## Pistes futures
|
|
||||||
|
|
||||||
- `git push --follow-tags` après `changeset publish` pour taguer les releases (pas fait par défaut aujourd'hui).
|
|
||||||
- Automatiser l'étape 2 du flux Changesets (PR "Version Packages" ouverte automatiquement) via un workflow `workflow_dispatch` ou planifié.
|
|
||||||
- Protection de branche réelle sur `main` côté Forgejo (l'interdiction de push direct n'est aujourd'hui que conventionnelle, via CLAUDE.md).
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
# Conventions
|
|
||||||
|
|
||||||
## Traçabilité des règles CLAUDE.md
|
|
||||||
|
|
||||||
| Règle CLAUDE.md | Mécanisme dans ce repo |
|
|
||||||
|---|---|
|
|
||||||
| Modificateurs d'accès explicites | Biome `useConsistentMemberAccessibility: "explicit"` (partiellement auto-fixable via `pnpm lint:fix`) ✅ |
|
|
||||||
| TSDoc sur chaque fonction/type/interface/classe/membre/méthode | TypeDoc en mode validation (`pnpm docs:check`, bloquant en CI). Biome n'a pas de règle équivalente ✅ (sauf `@lightweight-sdk/web-component`, exclu — voir `packages.md`) |
|
|
||||||
| `.catch()` obligatoire sur toute Promise non-awaited | Biome `noFloatingPromises` / `noMisusedPromises` / `useAwaitThenable` ✅ |
|
|
||||||
| `try/catch` obligatoire autour de chaque `await` individuel | **Aucun linter JS/TS courant ne vérifie ça** (ni Biome ni ESLint). Convention à appliquer soi-même à chaque nouveau package ❌ mécanisme automatique |
|
|
||||||
| Indentation "2 tabulations" | `biome.json` (`indentStyle: "tab"`, `indentWidth: 2`) + `.editorconfig` ⚠️ deux exceptions : les fichiers YAML n'acceptent pas les tabulations (interdit par la spec YAML) → 2 espaces ; le Markdown n'est pas formaté par Biome → 2 espaces par convention manuelle |
|
|
||||||
| Logging exhaustif | Convention à appliquer dans chaque nouveau package (pas de mécanisme automatique — à illustrer dans le code) |
|
|
||||||
| Séparation `.ts`/`.html`/`.scss` (Frontend) | S'applique aux futurs packages `client/` qui rendent des composants concrets avec leur propre template. Ne s'applique pas à `web-component`, qui est une bibliothèque d'abstraction sans template propre (et de toute façon non modifiée, voir `packages.md`) |
|
|
||||||
| Services modulaires autonomes (Backend) | Chaque package `server/` doit exposer sa propre API, indépendante des autres |
|
|
||||||
|
|
||||||
`pnpm lint` (= `biome check .`) tourne **sans** `--error-on-warnings` : les diagnostics de sévérité "warn" restent visibles mais ne font pas échouer la CI. Seules les erreurs bloquent.
|
|
||||||
|
|
||||||
## Imports
|
|
||||||
|
|
||||||
Sous `module: CommonJS` + `moduleResolution: Node10` (voir `architecture.md`), les imports relatifs **n'ont pas besoin** d'extension (`from "./foo"`, pas `from "./foo.js"`).
|
|
||||||
|
|
||||||
## Nommage
|
|
||||||
|
|
||||||
- Dossiers de package et noms npm : kebab-case (`@lightweight-sdk/my-package`).
|
|
||||||
- Classes : PascalCase. Fonctions/variables : camelCase.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- Colocalisés : `src/foo.ts` + `src/foo.test.ts`.
|
|
||||||
- Environnement Vitest par défaut : `"node"`. Pour un test qui a besoin du DOM, utiliser le pragma en première ligne du fichier : `// @vitest-environment happy-dom` (ajouter `happy-dom` en devDependency le jour où c'est nécessaire).
|
|
||||||
|
|
||||||
## Exemple : convention async/erreurs/logging attendue dans un nouveau package
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { logger } from './logger';
|
|
||||||
|
|
||||||
/** Fetches and parses the remote config, throwing on failure. */
|
|
||||||
export async function loadConfig(url: string): Promise<Config> {
|
|
||||||
try {
|
|
||||||
const response = await fetch(url);
|
|
||||||
return await response.json();
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('loadConfig failed', { url, error });
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Appel fire-and-forget : .catch() obligatoire (pas d'await)
|
|
||||||
void loadConfig(configUrl).catch((error) => logger.error('background config load failed', { error }));
|
|
||||||
```
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
# Registre des packages
|
|
||||||
|
|
||||||
| Package | Catégorie | Version | Statut |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `@lightweight-sdk/web-component` | client | 0.0.2 | migré depuis `lightweight-sdk-web-component`, non modifié |
|
|
||||||
|
|
||||||
## Ajouter un package
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm create-package <client|common|server> <nom-du-package>
|
|
||||||
```
|
|
||||||
|
|
||||||
Le script scaffolde `package.json`, `tsconfig.json`, `src/index.ts` + un test, un `README.md`, ajoute le package aux project references racine et met à jour ce tableau automatiquement. Reste à faire à la main : décrire le package, l'implémenter, et créer un changeset (`pnpm changeset`) quand il est prêt à être publié.
|
|
||||||
|
|
||||||
## Champs `package.json` obligatoires pour tout package du monorepo
|
|
||||||
|
|
||||||
- `name` (`@lightweight-sdk/<nom>`), `version`, `description`
|
|
||||||
- `main` (`dist/index.js`), `types` (`dist/index.d.ts`), `files: ["dist"]`
|
|
||||||
- `engines.node >= 22`
|
|
||||||
- `publishConfig.registry` pointant vers le registre npm privé (`https://git.dev.kyuno.fr/api/packages/kyuno/npm/`)
|
|
||||||
- `typedocOptions.entryPoints` (sauf exception documentée, voir ci-dessous)
|
|
||||||
|
|
||||||
## Observations sur `@lightweight-sdk/web-component` (non traitées, laissées telles quelles)
|
|
||||||
|
|
||||||
Le code source de ce package a été migré strictement à l'identique depuis l'ancien repo `lightweight-sdk-web-component`, sans aucune édition (décision explicite de l'auteur). Quelques points relevés à la lecture, non corrigés, à traiter soi-même dans une PR dédiée si souhaité :
|
|
||||||
|
|
||||||
- `connectedCallback()`/`disconnectedCallback()` (`abstractComponent.ts`), la constante `CUSTOM_COMPONENT_CONFIG` (`component.type.ts`) et `REQUEST_DOM_LINK_KEY` (`component.tools.ts`) n'ont pas de TSDoc complet — c'est pourquoi ce package est exclu de la validation `docs:check` dans `typedoc.json` (`exclude: ["packages/client/web-component/**"]`).
|
|
||||||
- Deux usages de `any` (`ConstructorFunction.new(...args: any[])`, `parseModuleToHtml(mod: any)`) — visibles comme avertissements Biome non bloquants (`noExplicitAny`).
|
|
||||||
- Le paramètre `constructor` dans `component.tools.ts` masque un nom global restreint (`noShadowRestrictedNames`) — désactivé pour ce package via un `overrides` dans `biome.json`.
|
|
||||||
- Le style de formatage d'origine (4 espaces, guillemets doubles) diffère de la config Biome du monorepo (tabulations, guillemets simples) — le formatter Biome est désactivé pour ce package via le même `overrides`.
|
|
||||||
- `REQUEST_DOM_LINK_KEY` semble ne plus être utilisé nulle part dans le code (le mécanisme actif est le tableau `__linkedProperties`) — potentiellement mort, mais pas supprimé.
|
|
||||||
- Quelques typos mineures ("atatched", "attriute") et un commentaire en français dans `LinkToDom` qui ne correspond plus exactement au comportement (`throw` au lieu d'un simple avertissement).
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2022",
|
|
||||||
"module": "CommonJS",
|
|
||||||
"moduleResolution": "Node10",
|
|
||||||
"strict": true,
|
|
||||||
"declaration": true,
|
|
||||||
"declarationMap": true,
|
|
||||||
"sourceMap": true,
|
|
||||||
"composite": true,
|
|
||||||
"incremental": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"experimentalDecorators": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
"files": [],
|
|
||||||
"references": [{ "path": "./packages/client/web-component" }]
|
|
||||||
}
|
|
||||||
27
typedoc.json
27
typedoc.json
|
|
@ -1,27 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://typedoc.org/schema.json",
|
|
||||||
"entryPointStrategy": "packages",
|
|
||||||
"entryPoints": ["packages/*/*"],
|
|
||||||
"out": "docs/api",
|
|
||||||
"validation": {
|
|
||||||
"notExported": true,
|
|
||||||
"invalidLink": true,
|
|
||||||
"notDocumented": true
|
|
||||||
},
|
|
||||||
"requiredToBeDocumented": [
|
|
||||||
"Enum",
|
|
||||||
"EnumMember",
|
|
||||||
"Variable",
|
|
||||||
"Function",
|
|
||||||
"Class",
|
|
||||||
"Interface",
|
|
||||||
"Property",
|
|
||||||
"Method",
|
|
||||||
"Accessor",
|
|
||||||
"TypeAlias",
|
|
||||||
"Parameter",
|
|
||||||
"Constructor"
|
|
||||||
],
|
|
||||||
"treatWarningsAsErrors": true,
|
|
||||||
"excludeExternals": true
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
import { defineConfig } from 'vitest/config';
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
test: {
|
|
||||||
include: ['packages/*/*/src/**/*.test.ts'],
|
|
||||||
exclude: ['**/node_modules/**', '**/dist/**'],
|
|
||||||
environment: 'node',
|
|
||||||
passWithNoTests: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Loading…
Reference in a new issue