generated from thedanchez/template-solidjs-library
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ccd2e22
commit ac605be
Showing
6 changed files
with
143 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,58 +1,54 @@ | ||
# Template: SolidJS Library | ||
<p> | ||
<img width="100%" src="https://assets.solidjs.com/banner?type=Ecosystem&background=tiles&project=solid-create-script" alt="solid-create-script"> | ||
</p> | ||
|
||
Template for [SolidJS](https://www.solidjs.com/) library package. Bundling of the library is managed by [tsup](https://tsup.egoist.dev/). | ||
# solid-create-script | ||
|
||
Other things configured include: | ||
Solid utility hook to dynamically load an external script. | ||
|
||
- Bun (for dependency management and running scripts) | ||
- TypeScript | ||
- ESLint / Prettier | ||
- Solid Testing Library + Vitest (for testing) | ||
- Playground app using library | ||
- GitHub Actions (for all CI/CD) | ||
### Installation | ||
|
||
## Getting Started | ||
```bash | ||
npm install solid-js solid-create-script | ||
pnpm add solid-js solid-create-script | ||
yarn add solid-js solid-create-script | ||
bun add solid-js solid-create-script | ||
``` | ||
|
||
Some pre-requisites before install dependencies: | ||
### Usage | ||
|
||
- Install Node Version Manager (NVM) | ||
```bash | ||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash | ||
``` | ||
- Install Bun | ||
```bash | ||
curl -fsSL https://bun.sh/install | bash | ||
``` | ||
```tsx | ||
const script = createScript("https://some-library.js"); | ||
const script = createScript("https://some-library.js", { async: true, ...scriptAttributes }); | ||
const script = createScript("https://some-library.js", { defer: true, ...scriptAttributes }); | ||
``` | ||
|
||
### Installing Dependencies | ||
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. | ||
|
||
```bash | ||
nvm use | ||
bun install | ||
``` | ||
When using `createScript`, here are some points to be aware of: | ||
|
||
### Local Development Build | ||
- The created `<script>` tag will be appeneded to `<head>`. | ||
- 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>`). | ||
- 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. | ||
|
||
```bash | ||
bun start | ||
``` | ||
### Full Example | ||
|
||
### Linting & Formatting | ||
```tsx | ||
import { Switch, Match } from "solid-js"; | ||
import { createScript } from "solid-create-script"; | ||
|
||
```bash | ||
bun run lint # checks source for lint violations | ||
bun run format # checks source for format violations | ||
function App() { | ||
const script = createScript("https://some-library.js"); | ||
|
||
bun run lint:fix # fixes lint violations | ||
bun run format:fix # fixes format violations | ||
return ( | ||
<Switch fallback={<ScriptProvider>...</ScriptProvider>}> | ||
<Match when={script.loading}>Loading Script...</Match> | ||
<Match when={script.error}>Failed to load script: {script.error.message}</Match> | ||
</Switch> | ||
); | ||
} | ||
``` | ||
|
||
### Contributing | ||
|
||
The only requirements when contributing are: | ||
### Feedback | ||
|
||
- You keep a clean git history in your branch | ||
- rebasing `main` instead of making merge commits. | ||
- Using proper commit message formats that adhere to [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) | ||
- Additionally, squashing (via rebase) commits that are not [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) | ||
- CI checks pass before merging into `main` | ||
Feel free to post any issues or suggestions to help improve this utility hook. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,40 @@ | ||
{ | ||
"name": "template-solidjs-library", | ||
"version": "0.0.0", | ||
"description": "Template for SolidJS library using tsup for bundling. Configured with Bun, NVM, TypeScript, ESLint, Prettier, Vitest, and GHA", | ||
"name": "solid-create-script", | ||
"version": "1.0.0", | ||
"description": "Solid utility hook to dynamically load an external script.", | ||
"type": "module", | ||
"main": "./dist/index.js", | ||
"module": "./dist/index.js", | ||
"types": "./dist/index.d.ts", | ||
"author": "Daniel Sanchez <[email protected]>", | ||
"license": "MIT", | ||
"homepage": "https://github.com/thedanchez/template-solidjs-library#readme", | ||
"homepage": "https://github.com/thedanchez/solid-create-script#readme", | ||
"bugs": { | ||
"url": "https://github.com/thedanchez/template-solidjs-library/issues" | ||
"url": "https://github.com/thedanchez/solid-create-script/issues" | ||
}, | ||
"exports": { | ||
"solid": "./dist/index.jsx", | ||
"import": { | ||
"types": "./dist/index.d.ts", | ||
"default": "./dist/index.js" | ||
} | ||
}, | ||
"browser": {}, | ||
"typesVersions": {}, | ||
"files": [ | ||
"dist", | ||
"README.md" | ||
], | ||
"keywords": [ | ||
"create", | ||
"create-script", | ||
"create-script-hook", | ||
"hook", | ||
"script", | ||
"Solid", | ||
"SolidJS", | ||
"use-script" | ||
], | ||
"scripts": { | ||
"build": "tsup", | ||
"build:watch": "tsup --watch", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,12 @@ | ||
import { createSignal } from "solid-js"; | ||
import { createScript } from "../src"; | ||
|
||
export const App = () => { | ||
const [count, setCount] = createSignal(0); | ||
const script = createScript("https://cdn.plaid.com/link/v2/stable/link-initialize.js", { defer: true }); | ||
|
||
return ( | ||
<div> | ||
<div>Playground App</div> | ||
<div>Count: {count()}</div> | ||
<button | ||
onClick={() => { | ||
setCount((prev) => prev + 1); | ||
}} | ||
> | ||
Increment Count | ||
</button> | ||
<div>Playground: solid-create-script</div> | ||
<div>Script Loading: {script.loading.toString()}</div> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { createResource, type JSX, splitProps } from "solid-js"; | ||
import type { DOMElement } from "solid-js/jsx-runtime"; | ||
|
||
type ScriptAttributes = Omit<JSX.ScriptHTMLAttributes<HTMLScriptElement>, "src">; | ||
|
||
type ScriptEvent = Event & { | ||
currentTarget: HTMLScriptElement; | ||
target: DOMElement; | ||
}; | ||
|
||
// Promise cache for script sources | ||
const SCRIPT_PROMISES = new Map<string, Promise<Event>>(); | ||
|
||
const loadScript = async (src: string, attributes: Readonly<ScriptAttributes>) => { | ||
const [initEvents, otherAttributes] = splitProps(attributes, ["onload", "onLoad", "onerror", "onError"]); | ||
|
||
// 1. Reject if no src provided | ||
if (!src) return Promise.reject(new Error('No "src" provided for createScript')); | ||
|
||
// 2. Return cached promise if referencing same src | ||
if (SCRIPT_PROMISES.has(src)) return SCRIPT_PROMISES.get(src)!; | ||
|
||
// 3. Check if script already exists (may have been added externally not via this hook) | ||
if (document.querySelector(`script[src="${src}"]`)) return Promise.resolve(new Event("already-loaded")); | ||
|
||
// 4. Create new script element | ||
const promise = new Promise<Event>((resolve, reject) => { | ||
const script = document.createElement("script"); | ||
script.src = src; | ||
|
||
script.onload = (e) => { | ||
if (typeof initEvents.onload === "function") { | ||
initEvents.onload(e as ScriptEvent); | ||
} else if (typeof initEvents.onLoad === "function") { | ||
initEvents.onLoad(e as ScriptEvent); | ||
} | ||
|
||
resolve(e); | ||
}; | ||
|
||
script.onerror = (e) => { | ||
if (typeof initEvents.onerror === "function") { | ||
initEvents.onerror(e as ScriptEvent); | ||
} else if (typeof initEvents.onError === "function") { | ||
initEvents.onError(e as ScriptEvent); | ||
} | ||
|
||
reject(e); | ||
}; | ||
|
||
// Apply additional attributes if any | ||
Object.entries(otherAttributes).forEach(([key, value]) => { | ||
script.setAttribute(key, value); | ||
}); | ||
|
||
document.head.appendChild(script); | ||
}); | ||
|
||
SCRIPT_PROMISES.set(src, promise); | ||
return promise; | ||
}; | ||
|
||
const createScript = (src: string, attributes: Readonly<ScriptAttributes> = {}) => { | ||
const [script] = createResource(() => loadScript(src, attributes)); | ||
return script; | ||
}; | ||
|
||
export default createScript; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1 @@ | ||
// Main library export site | ||
// Use playground app (via Vite) to test and document the library | ||
export { default as createScript } from "./createScript"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters