-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support usage of library with bundlers (#6)
* improving 'scripts/build-all.js' tool to build both 'index.js' (for non-bundler) and 'only.index.js' (for bundler), and to minify src/* scripts * adding bundler fields to 'package.json', and improve docs * more tweaks/fixes * removing outdated sodium.ready stuff * adding plugins for vite and webpack, lots of rearranging, updating docs * README: fixing links to bundler/non-bundler guides * fixing a few typos in BUNDLERS guide * fixing symlink destination locations in 'postinstall' script * fixing 'build-gh-pages.js' script to have the right 'dist/' directory * more fixes * package.json: removing some likely unnecessary fields * more fixes, improving build script and externals import * more fixes to scripts and testbed * fixing missing 'exports' for bundler plugins in 'package.json', docs tweaks * changing build tool to output 'bundlers/walc.mjs' instead of '.js' extension, updating docs/etc * adding more BUNDLERS docs about issues with top-level-await and SSR HTML injection failing, tweaks to code/tools
- Loading branch information
Showing
14 changed files
with
583 additions
and
115 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 |
---|---|---|
@@ -0,0 +1,123 @@ | ||
# Deploying WebAuthn-Local-Client WITH A Bundler | ||
|
||
This project has non-ESM dependencies, which unfortunately cannot be *bundled* in with your other app code. Modern bundlers unfortunately don't out-of-the-box support configurations that can handle such a situation. | ||
|
||
As such, this project provides plugins for Vite and Webpack, to take care of the various steps needed to get these non-ESM dependencies into an otherwise bundled web app built by those tools. | ||
|
||
## Bundler Plugins | ||
|
||
The plugins for Vite and Webpack are included in the `bundler-plugins/` directory. They should handle all necessary steps to load the dependencies. | ||
|
||
**Note:** You should not need to manually copy any files out of the `dist/bundlers/` directory, as the plugins access the `webauthn-local-client` dependency (in `node_modules`) directly to pull the files needed. But for reference, the files these plugins access are: | ||
|
||
* `dist/bundlers/walc.mjs` | ||
|
||
ESM library module that's suitable for bundling and `import`ing into your web app. | ||
|
||
**Note:** this is *not* the same as `dist/auto/walc.js`, which is only intended [for web application projects WITHOUT a bundler](NON-BUNDLERS.md) | ||
|
||
* `dist/bundlers/walc-external-bundle.js` | ||
|
||
Non-ESM (plain global .js) bundle of dependencies that must be loaded separately from (and prior to) your app's bundle. Includes the concatenated contents of these individual dependencies: | ||
|
||
- `dist/auto/external/libsodium.js` | ||
- `dist/auto/external/libsodium-wrappers.js` | ||
- `dist/auto/external/cbor.js` | ||
- `dist/auto/external/asn1.all.min.js` | ||
|
||
**Note:** The [`ASN1` dependency](https://github.com/yoursunny/asn1.js) is [licensed under MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/), which is generally compatible with this library's [MIT license](LICENSE.txt). However, MPL 2.0 specifically requires preservation of the copyright/license header (block comment at top of `asn1.all.min.js`). To comply with this licensing requirement, ensure your tooling does not remove this comment from the bundle file. | ||
|
||
### Vite Plugin | ||
|
||
If using Vite 5+, it's strongly suggested to import this library's Vite-plugin to manage the loading of its non-ESM dependencies. Add something like the following to your `vite.config.js` file: | ||
|
||
```js | ||
import { defineConfig } from "vite"; | ||
import WALC from "webauthn-local-client/bundlers/vite"; | ||
|
||
export default defineConfig({ | ||
// .. | ||
|
||
plugins: [ WALC() ], | ||
|
||
build: { | ||
// WALC uses "top-level await", which is ES2022+ | ||
target: "es2022" | ||
}, | ||
|
||
// .. | ||
}); | ||
``` | ||
|
||
This plugin works for the `vite dev` (dev-server), `vite preview` (also dev-server), and `vite build` modes. In all cases, it copies the `dist/bundlers/walc-external-bundle.js` file into the `public/` directory of your project root. It also injects a `<script src="/walc-external-bundle.js"></script>` tag into the markup of the `index.html` file that Vite produces for your app. | ||
|
||
**Note:** At present, this plugin is not configurable in any way (i.e., calling `WALC()` above with no arguments). If something about its behavior is not compatible with your Vite project setup -- which can vary widely and be quite complex to predict or support by a basic plugin -- it's recommended you simply copy over the `webauthn-local-client/bundler-plugins/vite.mjs` plugin and make necessary changes. | ||
|
||
#### Top-level `await` | ||
|
||
This library uses ["top-level `await`"](https://github.com/tc39/proposal-top-level-await), a feature added to JS in ES2022. The current default target for Vite seems to be browsers older than this, so the above config explicitly sets the `build.target` to `"es2022"`. | ||
|
||
You may experience issues where your tooling/configuration either ignores this setting, or otherwise breaks with it set. This may variously result in seeing an error about the top-level `await`s in this library being incompatible with the built-target, or an error about `await` needing to only be in `async function`s or the top-level of a module (which it is!). | ||
|
||
Those types of errors generally indicate that you may need to configure Vite to skip trying to optimize the `walc.mjs` file during bundling, something like: | ||
|
||
```js | ||
export default defineConfig({ | ||
|
||
// .. | ||
|
||
optimizeDeps: { | ||
exclude: [ "webauthn-local-client" ] | ||
} | ||
|
||
// .. | ||
}); | ||
``` | ||
|
||
#### SSR Breakage | ||
|
||
An unfortunate gotcha of tools that wrap Vite (e.g., Astro, Nuxt, etc) and do SSR (server-side rendering) is that they *break* a key assumption/behavior of this module's Vite plugin: the HTML injection of `<script src="/walc-external-bundle.js"></script>`. | ||
|
||
As such, you'll likely need to manually add that `<script>` tag to your HTML pages/templates. The Vite plugin still copies that file into the `public/` folder for you, so it should load once the tag is added to your HTML. | ||
|
||
### Webpack Plugin | ||
|
||
If using Webpack 5+, make sure you're already using the [HTML Webpack Plugin](https://github.com/jantimon/html-webpack-plugin/) to manage building your `index.html` (and/or other HTML pages). | ||
|
||
Then import this library's Webpack-plugin to manage the loading of its non-ESM dependencies. Add something like the following to your `webpack.config.js`: | ||
|
||
```js | ||
// 'HtmlWebpackPlugin' is a required dependency of the | ||
// webauthn-local-client Webpack plugin | ||
import HtmlWebpackPlugin from "html-webpack-plugin"; | ||
import WALC from "webauthn-local-client/bundlers/webpack"; | ||
|
||
export default { | ||
// .. | ||
|
||
plugins: [ | ||
// required WALC dependency | ||
new HtmlWebpackPlugin({ | ||
// .. | ||
}), | ||
|
||
WALC() | ||
], | ||
|
||
// .. | ||
}; | ||
``` | ||
|
||
This plugin copies the `dist/bundlers/walc-external-bundle.js` file into the build root (default `dist/`), along with the other bundled files. It also injects a `<script src="walc-external-bundle.js"></script>` tag into the markup of the `index.html` file (and any other HTML files) that Webpack produces for your app. | ||
|
||
**Note:** At present, this plugin is not configurable in any way (i.e., calling `WALC()` above with no arguments). If something about its behavior is not compatible with your Webpack project setup -- which can vary widely and be quite complex to predict or support by a basic plugin -- it's recommended you simply copy over the `webauthn-local-client/bundler-plugins/webpack.mjs` plugin and make necessary changes. | ||
|
||
## Import/Usage | ||
|
||
To import and use **webauthn-local-client** in a *bundled* browser app: | ||
|
||
```js | ||
import { register, auth } from "webauthn-local-client"; | ||
``` | ||
|
||
When `import`ed like this, both Vite and Webpack should (via these plugins) properly find and bundle the `dist/bundlers/walc.mjs` ESM library module with the rest of your app code, hopefully without any further steps necessary. |
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,49 @@ | ||
# Deploying WebAuthn-Local-Client WITHOUT A Bundler | ||
|
||
To use this library directly -- i.e., in a classic/vanilla web project without a modern bundler tool -- make a directory for it (e.g., `webauthn-local-client/`) in your browser app's JS assets directory. | ||
|
||
Then copy over all `dist/auto/*` contents, as-is: | ||
|
||
* `dist/auto/walc.js` | ||
|
||
**Note:** this is *not* the same as `dist/bundlers/walc.mjs`, which is only intended [for web application projects WITH a bundler](BUNDLERS.md) | ||
|
||
* `dist/auto/external.js` | ||
|
||
This is an *auto-loader* that dynamically loads the rest of the `external/*` dependencies via `<script>`-element injection into the DOM. `dist/auto/walc.js` imports and activates this loader automatically. | ||
|
||
* `dist/auto/external/*` (preserve the whole `external/` sub-directory): | ||
- `libsodium.js` | ||
- `libsodium-wrappers.js` | ||
- `cbor.js` | ||
- `asn1.all.min.js` | ||
|
||
## Import/Usage | ||
|
||
To import and use **webauthn-local-client** in a *non-bundled* browser app: | ||
|
||
```js | ||
import { register, auth } from "/path/to/js-assets/webauthn-local-client/walc.js"; | ||
``` | ||
|
||
The library's dependencies will be auto-loaded (via `external.js`). | ||
|
||
## Using Import Map | ||
|
||
If your **non-bundled** browser app has an [Import Map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap), you can improve the `import` by adding an entry for this library: | ||
|
||
```html | ||
<script type="importmap"> | ||
{ | ||
"imports": { | ||
"webauthn-local-client": "/path/to/js-assets/webauthn-local-client/walc.js" | ||
} | ||
} | ||
</script> | ||
``` | ||
|
||
Then you'll be able to `import` the library in a more friendly/readable way: | ||
|
||
```js | ||
import { register, auth } from "webauthn-local-client"; | ||
``` |
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
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,99 @@ | ||
import path from "node:path"; | ||
import fs from "node:fs"; | ||
import fsp from "node:fs/promises"; | ||
|
||
|
||
export default WALC; | ||
|
||
|
||
// ******************************** | ||
|
||
function WALC() { | ||
var config; | ||
var walcSrcPath; | ||
var externalBundleSrcPath; | ||
var externalBundleDestPath; | ||
var externalBundleCopied = false; | ||
|
||
return { | ||
name: "vite-plugin-walc", | ||
enforce: "pre", | ||
|
||
async configResolved(cfg) { | ||
config = cfg; | ||
var bundlersDir = path.join(config.root,"node_modules","webauthn-local-client","dist","bundlers"); | ||
walcSrcPath = path.join(bundlersDir,"walc.mjs"); | ||
externalBundleSrcPath = path.join(bundlersDir,"walc-external-bundle.js"); | ||
externalBundleDestPath = ( | ||
config.command == "build" ? | ||
path.join(config.root,config.build.outDir,path.basename(externalBundleSrcPath)) : | ||
|
||
config.command == "serve" ? | ||
path.join(config.publicDir,path.basename(externalBundleSrcPath)) : | ||
|
||
null | ||
); | ||
return copyExternalBundle(); | ||
}, | ||
resolveId(source) { | ||
// NOTE: this should never be `import`ed | ||
if (source == "webauthn-local-client/bundlers/walc-external-bundle.js") { | ||
// ...but if found, mark it as "external" because | ||
// the contents are non-ESM compatible | ||
return { id: source, external: true, }; | ||
} | ||
}, | ||
load(id,opts) { | ||
if (id == "webauthn-local-client") { | ||
return fs.readFileSync(walcSrcPath,{ encoding: "utf8", }); | ||
} | ||
}, | ||
|
||
buildEnd() { | ||
externalBundleCopied = false; | ||
}, | ||
transformIndexHtml(html) { | ||
return [ | ||
{ | ||
tag: "script", | ||
injectTo: "head-prepend", | ||
attrs: { | ||
src: `/${path.basename(externalBundleDestPath)}`, | ||
}, | ||
}, | ||
]; | ||
}, | ||
|
||
// NOTE: ensuring the external-bundle is copied (in case the | ||
// dest directory hasn't been created earlier in the lifecyle) | ||
writeBundle: copyExternalBundle, | ||
buildStart: copyExternalBundle, | ||
}; | ||
|
||
|
||
// **************************** | ||
|
||
async function copyExternalBundle() { | ||
if ( | ||
// need to copy the external bundle? | ||
!externalBundleCopied && | ||
|
||
// bundle output path set properly? | ||
externalBundleDestPath && | ||
|
||
// bundle file exists? | ||
fs.existsSync(externalBundleSrcPath) && | ||
|
||
// destination directory exists? | ||
fs.existsSync(path.dirname(externalBundleDestPath)) | ||
) { | ||
try { | ||
await fsp.copyFile(externalBundleSrcPath,externalBundleDestPath); | ||
externalBundleCopied = true; | ||
} | ||
catch (err) { | ||
console.log(err); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.