Skip to content

Commit 68b905b

Browse files
committed
feat: updates implementation to use @dschz/load-script
1 parent 2f1de2c commit 68b905b

File tree

9 files changed

+176
-212
lines changed

9 files changed

+176
-212
lines changed

.github/workflows/ci.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@ jobs:
1818
- name: Setup Bun
1919
uses: oven-sh/setup-bun@v1
2020
with:
21-
bun-version: latest
21+
bun-version: 1.2.12
2222
- name: Install Dependencies
2323
run: bun install --frozen-lockfile
24+
- name: Type Check
25+
run: bun run typecheck
2426
- name: Lint Check
2527
run: bun run lint
2628
- name: Format Check
2729
run: bun run format
30+
- name: Run Tests
31+
run: bun run test:cov

README.md

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,51 +6,81 @@
66

77
# solid-create-script
88

9-
Solid utility hook to dynamically load an external script.
9+
MIT Licensed
1010

11-
### Installation
11+
Utility function to dynamically load external scripts in both declarative and imperative styles within SolidJS.
12+
13+
## Installation
1214

1315
```bash
14-
npm install solid-js solid-create-script
15-
pnpm add solid-js solid-create-script
16-
yarn add solid-js solid-create-script
17-
bun add solid-js solid-create-script
16+
npm install solid-js @dschz/load-script solid-create-script
17+
pnpm install solid-js @dschz/load-script solid-create-script
18+
yarn install solid-js @dschz/load-script solid-create-script
19+
bun install solid-js @dschz/load-script solid-create-script
1820
```
1921

20-
### Usage
22+
> These are **peer dependencies**, so they must be installed manually:
23+
>
24+
> - `solid-js`
25+
> - `@dschz/load-script`
2126
22-
```tsx
23-
const script = createScript("https://some-library.js");
24-
const script = createScript("https://some-library.js", { async: true, ...scriptAttributes });
25-
const script = createScript("https://some-library.js", { defer: true, ...scriptAttributes });
27+
## Summary
28+
29+
```ts
30+
import createScript from "solid-create-script";
2631
```
2732

28-
Under the hood `createScript` makes use of the `createResource` Solid API when loading the desired script at the specified `src`. As such, the result of `createScript` is a `Resource<Event>` object that allows you to inspect when the script has finished loading or returned an error via `script.loading` and `script.error` respectively.
33+
## API Breakdown
34+
35+
### `createScript`
2936

30-
When using `createScript`, here are some points to be aware of:
37+
A lightweight wrapper around `loadScript` with `createResource` that returns a `Resource<HTMLScriptElement>`.
3138

32-
- The created `<script>` tag will be appeneded to `<head>`.
33-
- The created `<script>` tag will not be removed from the DOM when a component that uses this hook unmounts. (i.e. we do not make use of `onCleanup` to remove the `<script>` from `<head>`).
34-
- The hook will ensure no duplicate `<script>` tags referencing the same `src` will be created. Moreover, when multiple components reference the same `src`, they will all point to the same shared resource ensuring consistency within the reactive graph.
39+
The interface signature is as follows:
3540

36-
### Full Example
41+
```ts
42+
const createScript = (src: string, options?: LoadScriptOptions, container?: HTMLElement | null)
43+
```
44+
45+
The three arguments:
3746

38-
```tsx
39-
import { Switch, Match } from "solid-js";
40-
import { createScript } from "solid-create-script";
47+
- `src`: the script source to download
48+
- `options`: the options to pass to `loadScript`
49+
- `container`: the HTMLElement to append the `<script />` to.
4150

42-
function App() {
43-
const script = createScript("https://some-library.js");
51+
It is useful for:
52+
53+
- Conditionally rendering based on script load status
54+
- Tracking `loading`, `error`, and ready states
55+
56+
### Usage
57+
58+
```ts
59+
import { Switch, Match } from "solid-js"
60+
import createScript from "solid-create-script"
61+
62+
const CustomComponent = () => {
63+
const script = createScript("https://example.com/library.js", { async: true });
4464

4565
return (
46-
<Switch fallback={<ScriptProvider>...</ScriptProvider>}>
66+
<Switch>
4767
<Match when={script.loading}>Loading Script...</Match>
48-
<Match when={script.error}>Failed to load script: {script.error.message}</Match>
68+
<Match when={script.error}>Failed to load</Match>
69+
<Match when={script()}>Script is ready!</Match>
4970
</Switch>
50-
);
71+
)
5172
}
73+
5274
```
5375

54-
### Feedback
76+
> ⚠️ The `<script>` that is downloaded with `createScript` will be appended to `<head>`. This API currently does not support specifying a mount target for the `<script>` tag.
77+
78+
## Notes
79+
80+
- Scripts are automatically cached to prevent duplication.
81+
- The script is not removed on cleanup/unmount.
82+
- `createScript` uses `createResource` internally to manage async state.
83+
84+
## Feedback
5585

56-
Feel free to post any issues or suggestions to help improve this utility hook.
86+
Feel free to post issues or suggestions to help improve this library.

bun.lock

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
"vitest": "^2.1.9",
2525
},
2626
"peerDependencies": {
27-
"solid-js": "^1.9.6",
27+
"@dschz/load-script": ">=1.0.4",
28+
"solid-js": ">=1.8.0",
2829
},
2930
},
3031
},
@@ -135,6 +136,8 @@
135136

136137
"@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.3", "", {}, "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw=="],
137138

139+
"@dschz/load-script": ["@dschz/load-script@1.0.4", "", {}, "sha512-6XHrWt/HbQilI1CsA6HUVJ13R0TDNSpS/LrzEJS+6SARbc/37tMCEOOG4d0MLbsdmll0XhodEky7gJjC7+khRw=="],
140+
138141
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="],
139142

140143
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.4", "", { "os": "android", "cpu": "arm" }, "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ=="],

package.json

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,20 @@
2525
"dist"
2626
],
2727
"keywords": [
28-
"create",
28+
"solid",
29+
"solidjs",
30+
"solid-hook",
2931
"create-script",
30-
"create-script-hook",
31-
"hook",
32+
"use-script",
33+
"load-script",
34+
"inject-script",
35+
"dynamic-script",
36+
"script-loader",
37+
"script-injector",
38+
"external-script",
3239
"script",
33-
"Solid",
34-
"SolidJS",
35-
"use-script"
40+
"hook",
41+
"solid-create-script"
3642
],
3743
"scripts": {
3844
"build": "tsup",
@@ -72,6 +78,7 @@
7278
"vitest": "^2.1.9"
7379
},
7480
"peerDependencies": {
81+
"@dschz/load-script": ">=1.0.4",
7582
"solid-js": ">=1.8.0"
7683
}
7784
}

playground/App.tsx

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,36 @@
1+
import { Match, Switch } from "solid-js";
2+
13
import { createScript } from "../src";
24

5+
declare global {
6+
interface Window {
7+
google: unknown;
8+
}
9+
}
10+
311
export const App = () => {
4-
const script = createScript("https://cdn.plaid.com/link/v2/stable/link-initialize.js", {
5-
defer: true,
12+
const googleScript = createScript("https://www.gstatic.com/charts/loader.js", {
13+
async: true,
14+
type: "text/javascript",
15+
onLoad: () => console.log("Google Charts loaded"),
16+
onError: (e) => console.error("Google Charts failed", e),
617
});
718

819
return (
9-
<div>
10-
<div>Playground: solid-create-script</div>
11-
<div>Script Loading: {script.loading.toString()}</div>
20+
<div style={{ padding: "2rem", "font-family": "sans-serif" }}>
21+
<h1>solid-create-script Demo</h1>
22+
23+
<section style={{ "margin-top": "1.5rem" }}>
24+
<h2>createScript (Google Charts)</h2>
25+
<Switch>
26+
<Match when={googleScript.loading}>Loading Google Charts…</Match>
27+
<Match when={googleScript.error}>❌ Failed to load Google Charts</Match>
28+
<Match when={googleScript()}>
29+
✅ Script loaded. `google` is{" "}
30+
{typeof window.google !== "undefined" ? "defined" : "undefined"}.
31+
</Match>
32+
</Switch>
33+
</section>
1234
</div>
1335
);
1436
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { waitFor } from "@solidjs/testing-library";
2+
import { createRoot } from "solid-js";
3+
import { beforeEach, describe, expect, test } from "vitest";
4+
5+
import { createScript } from "../createScript";
6+
7+
const SCRIPT_SRC = "https://example.com/script.js";
8+
9+
describe("createScript", () => {
10+
beforeEach(() => {
11+
document.querySelectorAll("script").forEach((s) => s.remove());
12+
});
13+
14+
test("returns a SolidJS resource that resolves when the script loads", async () => {
15+
await createRoot(async (dispose) => {
16+
const scriptResource = createScript(SCRIPT_SRC);
17+
18+
const el = document.querySelector(`script[src="${SCRIPT_SRC}"]`);
19+
expect(el).toBeDefined();
20+
21+
el?.dispatchEvent(new Event("load"));
22+
23+
await waitFor(() => {
24+
expect(scriptResource()).toBeInstanceOf(HTMLScriptElement);
25+
});
26+
27+
expect(scriptResource!.loading).toBe(false);
28+
expect(scriptResource!.error).toBeUndefined();
29+
30+
dispose();
31+
});
32+
});
33+
});

0 commit comments

Comments
 (0)