@collagejs/svelte 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@ This is the official Svelte component library for *CollageJS* used to:
9
9
  1. Create *CollageJS* `CorePiece` objects out of Svelte components
10
10
  2. Consume `CorePiece` objects (made with any framework or library) in Svelte v5 projects
11
11
 
12
- ## Creating Svelte-Based `CorePiece` Objects
12
+ ## Creating Svelte-Powered `CorePiece` Objects
13
13
 
14
14
  Whenever we are creating a micro-frontend in Svelte v5 and wish for it to be used with *CollageJS*, we must create a `CorePiece` wrapper object for the Svelte component that is the root of our micro-frontend. Unless we wanted to take on this task ourselves, we use this package's `buildPiece()` function:
15
15
 
@@ -18,12 +18,23 @@ Whenever we are creating a micro-frontend in Svelte v5 and wish for it to be use
18
18
  import { buildPiece } from "@collagejs/svelte";
19
19
  // The component to wrap. It usually is App.svelte.
20
20
  import App from "./App.svelte";
21
+ // Automatic CSS mounting and unmounting algorithm:
22
+ import { cssMountFactory } from "@collagejs/vite-css/ex";
23
+
24
+ // Only one cssMount per file is needed, regardless of the number of factory functions.
25
+ const cssMount = cssMountFactory('mfe' /*, { options } */);
21
26
 
22
27
  export function myMfeFactory() {
23
- return buildPiece(App /*, { options } */);
28
+ const piece = buildPiece(App /*, { options } */);
29
+ export {
30
+ mount: [cssMount, piece.mount],
31
+ update: piece.update
32
+ }
24
33
  }
25
34
  ```
26
35
 
36
+ It is also customary to install [@collagejs/vite-css](https://github.com/collagejs/vite-css) in our piece-exporting projects to be able to mount the bundled CSS files that Vite produces. The CSS-mounting function features FOUC prevention, but it only works if `cssMount` is listed in the array before `piece.mount`. Remember this!
37
+
27
38
  > ✨ **Tip**: Repeat this process in the same or different file for any number of Svelte components that you wish to make available as independent micro-frontends. The sky is the limit.
28
39
 
29
40
  ## Consuming `CorePiece` Objects
@@ -42,3 +53,20 @@ We can mount *CollageJS* pieces created in any technology in Svelte projects by
42
53
  1. We must use the `piece()` function to pass the `CorePiece` object.
43
54
  2. The example uses the name of the factory function in the previous example, so we're mounting a Svelte MFE inside a Svelte project.
44
55
  3. The `"@my/bare-module-specifier"` module name is the bare module specifier we assign to the micro-frontend in our root project's import map.
56
+
57
+ ### Intellisense On The `CorePiece` Props
58
+
59
+ Your IDE can provide Intellisense on the properties the `CorePiece` given to `<Piece>` supports if the return value of the factory function is properly typed.
60
+
61
+ You can go several routes to type the factory functions. One of these is to create a `.d.ts` file and declare the ambient module:
62
+
63
+ ```typescript
64
+ import "@collagejs/core";
65
+
66
+ declare module "@my/bare-module-identifier" {
67
+ function myMfeFactory(): CorePiece<{ extra: string; data: boolean; }>;
68
+ // --------------------------------^ <-- Properties type
69
+ // Etc. Other factory functions for this module.
70
+ ...
71
+ }
72
+ ```
@@ -1,154 +1,154 @@
1
- <script module lang="ts">
2
- const piecePropsSymbol = Symbol();
3
-
4
- /**
5
- * Defines the special properties accepted by the `Piece` component. This type is to be used in conjunction with
6
- * the `piece()` helper function to create the special property required by the `Piece` component.
7
- */
8
- export type PieceProps<
9
- TProps extends Record<string, any> = Record<string, any>,
10
- > = {
11
- /**
12
- * The special property that contains the `CorePiece` instance to render, along with optional container
13
- * properties.
14
- */
15
- [piecePropsSymbol]: {
16
- /**
17
- * The `CorePiece` instance to render.
18
- */
19
- piece: CorePiece<TProps>;
20
- /**
21
- * Optional HTML attributes to set on the container element that wraps the piece.
22
- */
23
- containerProps?: HTMLAttributes<HTMLDivElement>;
24
- };
25
- };
26
-
27
- /**
28
- * Helper function to create the special property required by the `Piece` component. The result of this function
29
- * is to be spread into the props of the `Piece` component.
30
- *
31
- * @example
32
- * ```svelte
33
- * <Piece {...piece(myPiece, { onfocusin: focusInHandler })} foo="bar" count={42} />
34
- * ```
35
- *
36
- * @param piece The `CorePiece` instance to render.
37
- * @param containerProps Optional HTML attributes to set on the container element that wraps the piece.
38
- * @returns An object containing the special property to pass to the `Piece` component.
39
- */
40
- export function piece<
41
- TProps extends Record<string, any> = Record<string, any>,
42
- >(
43
- piece: CorePiece<TProps>,
44
- containerProps?: HTMLAttributes<HTMLDivElement>,
45
- ) {
46
- return {
47
- [piecePropsSymbol]: {
48
- piece,
49
- containerProps,
50
- },
51
- };
52
- }
53
- </script>
54
-
55
- <script
56
- lang="ts"
57
- generics="TProps extends Record<string, any> = Record<string, any>"
58
- >
59
- import { getCollageContext } from "../collageContext.js";
60
- import {
61
- mountPiece,
62
- type CorePiece,
63
- type MountedPiece,
64
- type MountPiece,
65
- } from "@collagejs/core";
66
- import { onMount } from "svelte";
67
- import type { HTMLAttributes } from "svelte/elements";
68
-
69
- type Props = TProps & PieceProps<TProps>;
70
-
71
- let { ...restProps }: Props = $props();
72
-
73
- const { piece: corePiece, containerProps } = $derived(restProps[piecePropsSymbol]);
74
- let containerEl: HTMLDivElement;
75
- let firstRun = true;
76
- const ctx = getCollageContext();
77
- let mountedPiece: MountedPiece<TProps> | undefined;
78
-
79
- onMount(() => {
80
- const mountPieceFn =
81
- (ctx?.mountPiece as MountPiece<TProps>) ?? mountPiece<TProps>;
82
- const mountPromise = mountPieceFn(corePiece, containerEl, restProps).then((mp) => {
83
- mountedPiece = mp;
84
- });
85
- return async () => {
86
- await mountPromise;
87
- await mountedPiece?.unmount();
88
- mountedPiece = undefined;
89
- };
90
- });
91
-
92
- $effect(() => {
93
- // Must be the first line so the dependency on restProps is tracked.
94
- const newProps = { ...restProps };
95
- delete (newProps as any)[piecePropsSymbol];
96
- if (firstRun) {
97
- firstRun = false;
98
- return;
99
- }
100
- mountedPiece?.update(newProps);
101
- });
102
- </script>
103
-
104
- <div bind:this={containerEl} {...containerProps}></div>
105
-
106
- <style>
107
- div {
108
- display: contents;
109
- }
110
- </style>
111
- <!--
112
- @component
113
-
114
- ### Piece Component
115
-
116
- Renders a CollageJS "piece" (micro-frontend) inside a Svelte application. The piece itself may be built using any
117
- valid framework or library, as long as it adheres to the *CollageJS* `CorePiece` interface.
118
-
119
- #### Props
120
-
121
- Properties passed to the `Piece` component must include a special property created using the `piece()` helper
122
- function. This property contains the `CorePiece` instance to render, along with optional container properties. One can
123
- also pass additional properties that will be forwarded to the mounted piece.
124
-
125
- If the mounted piece supports property updates, any changes to the additional properties will be propagated to the
126
- piece reactively as per the rules of Svelte's reactivity system.
127
-
128
- #### Example Usage
129
-
130
- 1. Import the `Piece` component and the `piece()` helper function from the library.
131
- 2. Import whatever you need to get a hold of your `CorePiece` instance. Normally, one imports a factory function that
132
- creates the piece from a *bare module specifier* (an alias defined by your application's import map):
133
- ```typescript
134
- import Piece, { piece } from '@collagejs/svelte';
135
- import { myPieceFactory } from '@my/bare-module-specifier';
136
- ```
137
- 3. Create a `CorePiece` instance using your factory function. Pass whatever arguments the factory function requires.
138
- 4. Render the `Piece` component in your Svelte template, passing the `CorePiece` instance using the `piece()` helper
139
- function. Also pass any additional properties that the piece supports:
140
-
141
- ```svelte
142
- <Piece {...piece(myPieceFactory(), { class: 'my-piece-container' })} foo="bar" count={42} />
143
- ```
144
-
145
- The above sets the `my-piece-container` CSS class on the container element that wraps the piece, and also passes the
146
- `foo` and `count` properties to the mounted piece. This example uses constant values in the properties, but you can
147
- also pass stateful values that can update reactively, as if it were a regular Svelte component.
148
-
149
- > ⚠️ **IMPORTANT**: Once a piece has unmounted, it cannot be remounted. You must create a new `CorePiece` instance
150
- > and `<Piece>` instance. This is by design. The easiest is to have the call to the factory function inside the
151
- > Svelte template, so that a new `CorePiece` instance is created each time a new `<Piece>` instance is created.
152
-
153
- Online Documentation: Pending (https://collagejs.dev)
154
- -->
1
+ <script module lang="ts">
2
+ const piecePropsSymbol = Symbol();
3
+
4
+ /**
5
+ * Defines the special properties accepted by the `Piece` component. This type is to be used in conjunction with
6
+ * the `piece()` helper function to create the special property required by the `Piece` component.
7
+ */
8
+ export type PieceProps<
9
+ TProps extends Record<string, any> = Record<string, any>,
10
+ > = {
11
+ /**
12
+ * The special property that contains the `CorePiece` instance to render, along with optional container
13
+ * properties.
14
+ */
15
+ [piecePropsSymbol]: {
16
+ /**
17
+ * The `CorePiece` instance to render.
18
+ */
19
+ piece: CorePiece<TProps> | Promise<CorePiece<TProps>>;
20
+ /**
21
+ * Optional HTML attributes to set on the container element that wraps the piece.
22
+ */
23
+ containerProps?: HTMLAttributes<HTMLDivElement>;
24
+ };
25
+ };
26
+
27
+ /**
28
+ * Helper function to create the special property required by the `Piece` component. The result of this function
29
+ * is to be spread into the props of the `Piece` component.
30
+ *
31
+ * @example
32
+ * ```svelte
33
+ * <Piece {...piece(myPiece, { onfocusin: focusInHandler })} foo="bar" count={42} />
34
+ * ```
35
+ *
36
+ * @param piece The `CorePiece` instance to render.
37
+ * @param containerProps Optional HTML attributes to set on the container element that wraps the piece.
38
+ * @returns An object containing the special property to pass to the `Piece` component.
39
+ */
40
+ export function piece<
41
+ TProps extends Record<string, any> = Record<string, any>,
42
+ >(
43
+ piece: CorePiece<TProps> | Promise<CorePiece<TProps>>,
44
+ containerProps?: HTMLAttributes<HTMLDivElement>,
45
+ ) {
46
+ return {
47
+ [piecePropsSymbol]: {
48
+ piece,
49
+ containerProps,
50
+ },
51
+ };
52
+ }
53
+ </script>
54
+
55
+ <script
56
+ lang="ts"
57
+ generics="TProps extends Record<string, any> = Record<string, any>"
58
+ >
59
+ import { getCollageContext } from "../collageContext.js";
60
+ import {
61
+ mountPiece,
62
+ type CorePiece,
63
+ type MountedPiece,
64
+ type MountPiece,
65
+ } from "@collagejs/core";
66
+ import { onMount } from "svelte";
67
+ import type { HTMLAttributes } from "svelte/elements";
68
+
69
+ type Props = TProps & PieceProps<TProps>;
70
+
71
+ let { ...restProps }: Props = $props();
72
+
73
+ const { piece: corePiece, containerProps } = $derived(restProps[piecePropsSymbol]);
74
+ let containerEl: HTMLDivElement;
75
+ let firstRun = true;
76
+ const ctx = getCollageContext();
77
+ let mountedPiece: MountedPiece<TProps> | undefined;
78
+
79
+ onMount(() => {
80
+ const mountPieceFn =
81
+ (ctx?.mountPiece as MountPiece<TProps>) ?? mountPiece<TProps>;
82
+ const mountPromise = mountPieceFn(corePiece, containerEl, restProps).then((mp) => {
83
+ mountedPiece = mp;
84
+ });
85
+ return async () => {
86
+ await mountPromise;
87
+ await mountedPiece?.unmount();
88
+ mountedPiece = undefined;
89
+ };
90
+ });
91
+
92
+ $effect(() => {
93
+ // Must be the first line so the dependency on restProps is tracked.
94
+ const newProps = { ...restProps };
95
+ delete (newProps as any)[piecePropsSymbol];
96
+ if (firstRun) {
97
+ firstRun = false;
98
+ return;
99
+ }
100
+ mountedPiece?.update(newProps);
101
+ });
102
+ </script>
103
+
104
+ <div bind:this={containerEl} {...containerProps}></div>
105
+
106
+ <style>
107
+ div {
108
+ display: contents;
109
+ }
110
+ </style>
111
+ <!--
112
+ @component
113
+
114
+ ### Piece Component
115
+
116
+ Renders a CollageJS "piece" (micro-frontend) inside a Svelte application. The piece itself may be built using any
117
+ valid framework or library, as long as it adheres to the *CollageJS* `CorePiece` interface.
118
+
119
+ #### Props
120
+
121
+ Properties passed to the `Piece` component must include a special property created using the `piece()` helper
122
+ function. This property contains the `CorePiece` instance to render, along with optional container properties. One can
123
+ also pass additional properties that will be forwarded to the mounted piece.
124
+
125
+ If the mounted piece supports property updates, any changes to the additional properties will be propagated to the
126
+ piece reactively as per the rules of Svelte's reactivity system.
127
+
128
+ #### Example Usage
129
+
130
+ 1. Import the `Piece` component and the `piece()` helper function from the library.
131
+ 2. Import whatever you need to get a hold of your `CorePiece` instance. Normally, one imports a factory function that
132
+ creates the piece from a *bare module specifier* (an alias defined by your application's import map):
133
+ ```typescript
134
+ import Piece, { piece } from '@collagejs/svelte';
135
+ import { myPieceFactory } from '@my/bare-module-specifier';
136
+ ```
137
+ 3. Create a `CorePiece` instance using your factory function. Pass whatever arguments the factory function requires.
138
+ 4. Render the `Piece` component in your Svelte template, passing the `CorePiece` instance using the `piece()` helper
139
+ function. Also pass any additional properties that the piece supports:
140
+
141
+ ```svelte
142
+ <Piece {...piece(myPieceFactory(), { class: 'my-piece-container' })} foo="bar" count={42} />
143
+ ```
144
+
145
+ The above sets the `my-piece-container` CSS class on the container element that wraps the piece, and also passes the
146
+ `foo` and `count` properties to the mounted piece. This example uses constant values in the properties, but you can
147
+ also pass stateful values that can update reactively, as if it were a regular Svelte component.
148
+
149
+ > ⚠️ **IMPORTANT**: Once a piece has unmounted, it cannot be remounted. You must create a new `CorePiece` instance
150
+ > and `<Piece>` instance. This is by design. The easiest is to have the call to the factory function inside the
151
+ > Svelte template, so that a new `CorePiece` instance is created each time a new `<Piece>` instance is created.
152
+
153
+ Online Documentation: Pending (https://collagejs.dev)
154
+ -->
@@ -12,7 +12,7 @@ export type PieceProps<TProps extends Record<string, any> = Record<string, any>>
12
12
  /**
13
13
  * The `CorePiece` instance to render.
14
14
  */
15
- piece: CorePiece<TProps>;
15
+ piece: CorePiece<TProps> | Promise<CorePiece<TProps>>;
16
16
  /**
17
17
  * Optional HTML attributes to set on the container element that wraps the piece.
18
18
  */
@@ -32,9 +32,9 @@ export type PieceProps<TProps extends Record<string, any> = Record<string, any>>
32
32
  * @param containerProps Optional HTML attributes to set on the container element that wraps the piece.
33
33
  * @returns An object containing the special property to pass to the `Piece` component.
34
34
  */
35
- export declare function piece<TProps extends Record<string, any> = Record<string, any>>(piece: CorePiece<TProps>, containerProps?: HTMLAttributes<HTMLDivElement>): {
35
+ export declare function piece<TProps extends Record<string, any> = Record<string, any>>(piece: CorePiece<TProps> | Promise<CorePiece<TProps>>, containerProps?: HTMLAttributes<HTMLDivElement>): {
36
36
  [piecePropsSymbol]: {
37
- piece: CorePiece<TProps>;
37
+ piece: CorePiece<TProps> | Promise<CorePiece<TProps>>;
38
38
  containerProps: HTMLAttributes<HTMLDivElement> | undefined;
39
39
  };
40
40
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@collagejs/svelte",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Svelte v5 integration for the CollageJS micro-frontend library",
5
5
  "author": {
6
6
  "name": "José Pablo Ramírez Vargas",
@@ -25,6 +25,7 @@
25
25
  "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
26
26
  "test:unit": "vitest",
27
27
  "test": "npm run test:unit -- --run",
28
+ "test:cicd": "npx playwright install && npm run test",
28
29
  "test:e2e": "playwright test"
29
30
  },
30
31
  "files": [
@@ -46,8 +47,8 @@
46
47
  }
47
48
  },
48
49
  "peerDependencies": {
49
- "svelte": "^5.0.0",
50
- "@collagejs/core": "^0.1.0"
50
+ "@collagejs/core": "0.x || 1.x",
51
+ "svelte": "^5.0.0"
51
52
  },
52
53
  "devDependencies": {
53
54
  "@playwright/test": "^1.57.0",