From 30295d2d0dc077eaec6fe8619c0cc42f6aa418d0 Mon Sep 17 00:00:00 2001 From: JackNoordhuis Date: Mon, 25 Sep 2023 18:46:14 +1000 Subject: [PATCH 1/3] fix typedoc docs building * Build docs from generated types files --- package.json | 4 ++-- tsconfig.docs.json | 10 ++++++++++ typedoc.json | 10 ++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 tsconfig.docs.json diff --git a/package.json b/package.json index 2189880..1efa023 100644 --- a/package.json +++ b/package.json @@ -35,8 +35,8 @@ "build": "npm run clean && tsc --build tsconfig.json --inlineSourceMap", "dev": "npm run build -- --watch", "build-dist": "npm run build -- --inlineSourceMap --listEmittedFiles", - "docs": "npx typedoc --plugin typedoc-plugin-extras --out docs-build --cacheBust src/index.ts", - "docs-dist": "npm run docs -- --gitRevision dev", + "docs": "npx typedoc", + "docs-dist": "npm run docs -- --gitRevision dist", "setup-examples": "cd examples/package && npm run setup-dev && cd ../project && npm run setup-dev", "build-examples": "cd examples/package && npm run build && cd ../project && npm run build" }, diff --git a/tsconfig.docs.json b/tsconfig.docs.json new file mode 100644 index 0000000..be5512d --- /dev/null +++ b/tsconfig.docs.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./tsconfig.json", + "include": [ + "types/**/*.d.ts" + ], + "compilerOptions": { + "noEmit": true + } +} \ No newline at end of file diff --git a/typedoc.json b/typedoc.json index 917daab..3ea2aec 100644 --- a/typedoc.json +++ b/typedoc.json @@ -1,4 +1,14 @@ { + "entryPoints": [ + "types/index.d.ts" + ], + "tsconfig": "tsconfig.docs.json", + "emit": "docs", + "out": "./docs-build", + "cacheBust": true, + "plugin": [ + "typedoc-plugin-extras" + ], "favicon": "https://nxtlvlsoftware.github.io/alpine-typescript/favicon.ico", "footerTypedocVersion": true, "footerDate": true, From 226f6c5f0eec5336945eb320f9fec9ac6da62431 Mon Sep 17 00:00:00 2001 From: JackNoordhuis Date: Mon, 25 Sep 2023 18:51:51 +1000 Subject: [PATCH 2/3] Publish pages workflow: fix build * Wait on a single job for package publish success (avoids spamming github api) * Use new package.json script commands --- .github/workflows/pages-publish.yml | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/pages-publish.yml b/.github/workflows/pages-publish.yml index 0206419..06e0777 100644 --- a/.github/workflows/pages-publish.yml +++ b/.github/workflows/pages-publish.yml @@ -16,8 +16,8 @@ env: NODE_VERSION: 20 jobs: - build-docs: - name: Generate site + wait-for-package-publish: + name: Wait for package publish workflow success runs-on: ubuntu-latest timeout-minutes: 5 @@ -30,6 +30,13 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} wait-interval: 10 + build-docs: + name: Generate site + needs: [wait-for-package-publish] + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: - uses: actions/checkout@v4 - name: Remove README.md index lines @@ -48,10 +55,10 @@ jobs: key: ${{ runner.os }}}-${{ env.NODE_VERSION }}-node_modules-${{ hashFiles('package.json') }} - name: Install dependencies - run: npm install + run: npm run clean-install - - name: Build alpine-typescript - run: npm run docs-ci + - name: Build alpine-typescript docs + run: npm run docs-dist - name: Copy favicon to /docs-build run: | @@ -64,18 +71,11 @@ jobs: build-example: name: Generate example site + needs: [wait-for-package-publish] runs-on: ubuntu-latest timeout-minutes: 5 steps: - - name: Wait for publish success - uses: lewagon/wait-on-check-action@v1.3.1 - with: - ref: ${{ github.ref }} - check-name: Publish to npm registry - repo-token: ${{ secrets.GITHUB_TOKEN }} - wait-interval: 10 - - uses: actions/checkout@v3 with: sparse-checkout: | @@ -103,10 +103,10 @@ jobs: key: ${{ runner.os }}}-${{ env.NODE_VERSION }}-node_modules-${{ hashFiles('package.json') }} - name: Install dependencies - run: npm install + run: npm clean-install - name: Build example project - run: npm run build-ci + run: npm run build-dist - uses: actions/upload-artifact@v3 with: From 139d1c07443482932e8a84a73c61f724a65757d3 Mon Sep 17 00:00:00 2001 From: JackNoordhuis Date: Mon, 25 Sep 2023 08:53:42 +0000 Subject: [PATCH 3/3] [create-pull-request] update tsc build dist files --- dist/Component.js | 41 +++++++ dist/Global.js | 29 +++++ dist/Plugin.js | 75 +++++++++++++ dist/Store.js | 201 +++++++++++++++++++++++++++++++++ dist/index.js | 13 +++ src/Component.ts | 161 --------------------------- src/Global.ts | 73 ------------ src/Plugin.ts | 109 ------------------ src/Store.ts | 256 ------------------------------------------- src/index.ts | 35 ------ types/Component.d.ts | 135 +++++++++++++++++++++++ types/Global.d.ts | 60 ++++++++++ types/Plugin.d.ts | 56 ++++++++++ types/Store.d.ts | 66 +++++++++++ types/index.d.ts | 12 ++ 15 files changed, 688 insertions(+), 634 deletions(-) create mode 100644 dist/Component.js create mode 100644 dist/Global.js create mode 100644 dist/Plugin.js create mode 100644 dist/Store.js create mode 100644 dist/index.js delete mode 100644 src/Component.ts delete mode 100644 src/Global.ts delete mode 100644 src/Plugin.ts delete mode 100644 src/Store.ts delete mode 100644 src/index.ts create mode 100644 types/Component.d.ts create mode 100644 types/Global.d.ts create mode 100644 types/Plugin.d.ts create mode 100644 types/Store.d.ts create mode 100644 types/index.d.ts diff --git a/dist/Component.js b/dist/Component.js new file mode 100644 index 0000000..d9eb78a --- /dev/null +++ b/dist/Component.js @@ -0,0 +1,41 @@ +/** + * Light-weight interface for class based components. + * + * Provides property declarations for Alpine magics that will exist when + * used as an Alpine component. + * + * Property declarations copied from @types/alpinejs. + * + * {@link https://www.npmjs.com/package/@types/alpinejs} + * + * @public + */ +var AlpineComponent = /** @class */ (function () { + function AlpineComponent() { + } + /** + * Declare an object as an x-bind property for this component. + * + * Use this method to define properties for use with x-bind: + * ```typescript + * protected myBinding = this.binding({ + * ["@click.prevent"]() { console.log("click prevented!") } + * }); + * ``` + * + * @protected + * + * @template HiddenKeys Define accessible properties (protected/private) + * that are not included by `keyof` + * + * @param obj The object for use with x-bind + * + * @return The same object passed to {@link obj} + */ + AlpineComponent.prototype.binding = function (obj) { + return obj; + }; + return AlpineComponent; +}()); +export { AlpineComponent }; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL0NvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFxRUE7Ozs7Ozs7Ozs7O0dBV0c7QUFDSDtJQUFBO0lBK0VBLENBQUM7SUF2QkE7Ozs7Ozs7Ozs7Ozs7Ozs7OztPQWtCRztJQUNPLGlDQUFPLEdBQWpCLFVBQWtELEdBQXVEO1FBQ3hHLE9BQU8sR0FBRyxDQUFDO0lBQ1osQ0FBQztJQUVGLHNCQUFDO0FBQUQsQ0FBQyxBQS9FRCxJQStFQyJ9 \ No newline at end of file diff --git a/dist/Global.js b/dist/Global.js new file mode 100644 index 0000000..914352e --- /dev/null +++ b/dist/Global.js @@ -0,0 +1,29 @@ +/** + * Check if an {@link Alpine} object has the components properties. + * + * @public + * + * @param obj The Alpine object to check + * + * @return True if component properties are injected, false otherwise. + */ +export function satisfiesAlpineWithComponents(obj) { + // @ts-ignore + return !!(obj.Components && obj.component); +} +/** + * Cast an {@link Alpine} object to {@link AlpineWithComponents} if it + * has the injected properties. + * + * @public + * + * @param obj The Alpine object to cast + * + * @return The object cast to {@link AlpineWithComponents} if properties are + * injected, null otherwise. + */ +export function castToAlpineWithComponents(obj) { + if (obj === void 0) { obj = window.Alpine; } + return satisfiesAlpineWithComponents(obj) ? obj : null; +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiR2xvYmFsLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL0dsb2JhbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUErQkE7Ozs7Ozs7O0dBUUc7QUFDSCxNQUFNLFVBQVUsNkJBQTZCLENBQUMsR0FBVztJQUN4RCxhQUFhO0lBQ2IsT0FBTyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsVUFBVSxJQUFJLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7R0FVRztBQUNILE1BQU0sVUFBVSwwQkFBMEIsQ0FBQyxHQUEyQjtJQUEzQixvQkFBQSxFQUFBLE1BQWMsTUFBTSxDQUFDLE1BQU07SUFDckUsT0FBTyw2QkFBNkIsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQXVCLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO0FBQzlFLENBQUMifQ== \ No newline at end of file diff --git a/dist/Plugin.js b/dist/Plugin.js new file mode 100644 index 0000000..48405e6 --- /dev/null +++ b/dist/Plugin.js @@ -0,0 +1,75 @@ +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +import { ComponentStore } from './Store'; +export var AlpineComponents; +(function (AlpineComponents) { + /** + * Default bootstrap options. + * + * Assumes production environment. + * + * @public + */ + AlpineComponents.defaultOptions = { + components: {}, + bootstrapAlpine: false, + startAlpine: true, + logErrors: false + }; + /** + * Bootstrap the components package. + * + * @public + * + * @param options Provided options (defaults applied to missing values {@link defaultOptions}.) + * @param alpine The Alpine instance to use (defaults to window.Alpine or creates Alpine when + * the bootstrapAlpine option is set) + */ + function bootstrap(options, alpine) { + if (options === void 0) { options = AlpineComponents.defaultOptions; } + if (alpine === void 0) { alpine = window.Alpine; } + var opts = __assign(__assign({}, AlpineComponents.defaultOptions), options); + if (opts.bootstrapAlpine && alpine !== undefined) { + if (opts.logErrors) { + console.error('Cannot bootstrap Alpine when window.Alpine is already defined.'); + } + return; + } + Promise.resolve(opts.bootstrapAlpine ? + import('alpinejs').then(function (imp) { return imp.default; }) : alpine).then(function (alpine) { + if (opts.bootstrapAlpine) { + window.Alpine = alpine; + } + window.AlpineComponents = new ComponentStore(alpine, opts.components, opts.logErrors); + if (opts.startAlpine) { + alpine.start(); + } + }); + } + AlpineComponents.bootstrap = bootstrap; +})(AlpineComponents || (AlpineComponents = {})); +/** + * Export a function to be used with `Alpine.plugin()`. + * + * @public + * + * Calls {@link AlpineComponents.bootstrap} with sensible options. + * + * @param alpine + */ +export function componentsPlugin(alpine) { + AlpineComponents.bootstrap({ + bootstrapAlpine: false, + startAlpine: false + }, alpine); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGx1Z2luLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL1BsdWdpbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7OztBQUVBLE9BQU8sRUFFTixjQUFjLEVBQ2QsTUFBTSxTQUFTLENBQUM7QUFFakIsTUFBTSxLQUFXLGdCQUFnQixDQXFGaEM7QUFyRkQsV0FBaUIsZ0JBQWdCO0lBNEJoQzs7Ozs7O09BTUc7SUFDVSwrQkFBYyxHQUFZO1FBQ3RDLFVBQVUsRUFBRSxFQUFFO1FBRWQsZUFBZSxFQUFFLEtBQUs7UUFDdEIsV0FBVyxFQUFFLElBQUk7UUFFakIsU0FBUyxFQUFFLEtBQUs7S0FDaEIsQ0FBQztJQUVGOzs7Ozs7OztPQVFHO0lBQ0gsU0FBZ0IsU0FBUyxDQUN4QixPQUEwQyxFQUMxQyxNQUFxQztRQURyQyx3QkFBQSxFQUFBLFVBQTRCLCtCQUFjO1FBQzFDLHVCQUFBLEVBQUEsU0FBd0IsTUFBTSxDQUFDLE1BQU07UUFFckMsSUFBTSxJQUFJLHlCQUNOLGlCQUFBLGNBQWMsR0FDZCxPQUFPLENBQ1YsQ0FBQztRQUVGLElBQUksSUFBSSxDQUFDLGVBQWUsSUFBSSxNQUFNLEtBQUssU0FBUyxFQUFFO1lBQ2pELElBQUksSUFBSSxDQUFDLFNBQVMsRUFBRTtnQkFDbkIsT0FBTyxDQUFDLEtBQUssQ0FBQyxnRUFBZ0UsQ0FBQyxDQUFDO2FBQ2hGO1lBQ0QsT0FBTztTQUNQO1FBRUQsT0FBTyxDQUFDLE9BQU8sQ0FDZCxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7WUFDckIsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDLElBQUksQ0FBQyxVQUFDLEdBQUcsSUFBSyxPQUFBLEdBQUcsQ0FBQyxPQUFPLEVBQVgsQ0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FDdkQsQ0FBQyxJQUFJLENBQUMsVUFBQyxNQUFxQjtZQUM1QixJQUFJLElBQUksQ0FBQyxlQUFlLEVBQUU7Z0JBQ3pCLE1BQU0sQ0FBQyxNQUFNLEdBQWlDLE1BQU0sQ0FBQzthQUNyRDtZQUVELE1BQU0sQ0FBQyxnQkFBZ0IsR0FBRyxJQUFJLGNBQWMsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7WUFFdEYsSUFBSSxJQUFJLENBQUMsV0FBVyxFQUFFO2dCQUNyQixNQUFNLENBQUMsS0FBSyxFQUFFLENBQUM7YUFDZjtRQUNGLENBQUMsQ0FBQyxDQUFDO0lBQ0osQ0FBQztJQTlCZSwwQkFBUyxZQThCeEIsQ0FBQTtBQUVGLENBQUMsRUFyRmdCLGdCQUFnQixLQUFoQixnQkFBZ0IsUUFxRmhDO0FBRUQ7Ozs7Ozs7O0dBUUc7QUFDSCxNQUFNLFVBQVUsZ0JBQWdCLENBQUMsTUFBc0I7SUFDdEQsZ0JBQWdCLENBQUMsU0FBUyxDQUFDO1FBQzFCLGVBQWUsRUFBRSxLQUFLO1FBQ3RCLFdBQVcsRUFBRSxLQUFLO0tBQ2xCLEVBQUUsTUFBTSxDQUFDLENBQUM7QUFDWixDQUFDIn0= \ No newline at end of file diff --git a/dist/Store.js b/dist/Store.js new file mode 100644 index 0000000..7066f9a --- /dev/null +++ b/dist/Store.js @@ -0,0 +1,201 @@ +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +import { AlpineComponent } from './Component'; +/** + * @see https://www.w3schools.com/js/js_reserved.asp + * + * @internal + */ +var ReservedNames = ['abstract', 'arguments', 'await', 'boolean', 'break', 'byte', 'case', + 'catch', 'char', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'double', 'else', + 'enum', 'eval', 'export', 'extends', 'false', 'final', 'finally', 'float', 'for', 'function', 'goto', + 'if', 'implements', 'import', 'in', 'instanceof', 'int', 'interface', 'let', 'long', 'native', 'new', + 'null', 'package', 'private', 'protected', 'public', 'return', 'short', 'static', 'super', 'switch', + 'this', 'throw', 'throws', 'transient', 'true', 'try', 'typeof', 'var', 'void', 'volatile', 'while', + 'with', 'yield']; +/** + * Internal component registration failure reasons. + * + * @internal + */ +var RegisterComponentFailure; +(function (RegisterComponentFailure) { + RegisterComponentFailure[RegisterComponentFailure["GenericMustHaveFunctionAsSecond"] = 0] = "GenericMustHaveFunctionAsSecond"; + RegisterComponentFailure[RegisterComponentFailure["NameMustBeProvidedForComponentWithNoDefault"] = 1] = "NameMustBeProvidedForComponentWithNoDefault"; + RegisterComponentFailure[RegisterComponentFailure["UnknownArgumentTypes"] = 2] = "UnknownArgumentTypes"; + RegisterComponentFailure[RegisterComponentFailure["ReservedName"] = 3] = "ReservedName"; +})(RegisterComponentFailure || (RegisterComponentFailure = {})); +var ComponentStore = /** @class */ (function () { + function ComponentStore(alpinejs, components, logErrors) { + var _this = this; + if (components === void 0) { components = {}; } + if (logErrors === void 0) { logErrors = false; } + this.logErrors = logErrors; + this.initialized = false; + this.components = {}; + this.alpine = alpinejs; + this.alpine.Components = this; + this.alpine.component = this.component; + Object.entries(components).forEach(function (_a) { + var name = _a[0], component = _a[1]; + _this.register(name, component); + }); + window.addEventListener('alpine:init', function () { + _this.init(); + }); + } + ComponentStore.prototype.init = function () { + var _this = this; + if (this.initialized) { + return; + } + document.dispatchEvent(new CustomEvent('alpine-components:init')); + Object.entries(this.components) + .forEach(function (_a) { + var name = _a[0]; + return _this.registerConstructorAsAlpineData(name); + }); + this.initialized = true; + }; + /** + * Retrieve a registered component constructor. + * + * @param name The component name + * + * @return ComponentConstructor + * + * If registered, returns a callable that accepts the component constructor arguments + * and creates the component object. Returns undefined if not registered. + */ + ComponentStore.prototype.component = function (name) { + // @ts-ignore + return this.components[name]; + }; + ComponentStore.prototype.registerAll = function (components) { + var _this = this; + Object.entries(components) + .forEach(function (_a) { + var name = _a[0], component = _a[1]; + return _this.register(name, component); + }); + }; + ComponentStore.prototype.register = function ( + // @ts-expect-error TS3244 + nameOrComponentClass, constructorOrComponentName) { + if (constructorOrComponentName === void 0) { constructorOrComponentName = ''; } + var component; + if (typeof nameOrComponentClass === 'string') { // register generic object (normal alpine data) + if (typeof constructorOrComponentName === 'string') { + this.logRegisterFailure(RegisterComponentFailure.GenericMustHaveFunctionAsSecond); + return; + } + component = ComponentStore.getObjectData(nameOrComponentClass, constructorOrComponentName); + } + else if (typeof nameOrComponentClass === 'function') { // register class as component + component = ComponentStore.getClassData(nameOrComponentClass, constructorOrComponentName); + if (component.name === "") { + this.logRegisterFailure(RegisterComponentFailure.NameMustBeProvidedForComponentWithNoDefault); + } + } + else { + this.logRegisterFailure(RegisterComponentFailure.UnknownArgumentTypes); + return; + } + if (ReservedNames.includes(component.name)) { + this.logRegisterFailure(RegisterComponentFailure.ReservedName); + } + this.components[component.name] = component.constructor; + if (this.initialized) { + this.registerConstructorAsAlpineData(component.name); + } + }; + /** + * Register a component to Alpine through Alpine.data(). + * + * @param name The name of the component (must already be registered to the store.) + */ + ComponentStore.prototype.registerConstructorAsAlpineData = function (name) { + this.alpine.data(name, this.component(name)); + }; + ComponentStore.getObjectData = function (name, component) { + return { + name: name, + constructor: ((component.prototype instanceof AlpineComponent) ? + // @ts-ignore + makeAlpineConstructor(component) : component) + }; + }; + ComponentStore.getClassData = function (component, name) { + var resolvedName = (name !== undefined ? name : component.prototype.name); + return { + name: resolvedName, + constructor: makeAlpineConstructor(component) + }; + }; + ComponentStore.prototype.logRegisterFailure = function (reason) { + if (!this.logErrors) { + return; + } + switch (reason) { + case RegisterComponentFailure.GenericMustHaveFunctionAsSecond: + console.error("Second argument must be a constructor function for component."); + break; + case RegisterComponentFailure.NameMustBeProvidedForComponentWithNoDefault: + // should be impossible because we fall back to prototype name + console.error("Component name must be provided when class doesn't specify a default."); + break; + case RegisterComponentFailure.UnknownArgumentTypes: + console.error("Cannot register component with provided argument types. Check Typescript definitions for usage."); + break; + case RegisterComponentFailure.ReservedName: + console.error("Cannot register component with name matching a reserved keyword."); + break; + } + }; + return ComponentStore; +}()); +export { ComponentStore }; +/** + * Copy prototype functions and object properties to an empty object. + * + * @param instance The object to copy functions and properties from + */ +export function transformToAlpineData(instance) { + var methodNames = []; + for (var prototype = Object.getPrototypeOf(instance); prototype.constructor.name !== 'Object'; prototype = Object.getPrototypeOf(prototype)) { + Object.getOwnPropertyNames(prototype).forEach(function (name) { + if (methodNames.includes(name)) { + return; + } + methodNames.push(name); + }); + } + return __spreadArray(__spreadArray([], methodNames, true), Object.getOwnPropertyNames(instance) // properties + , true).reduce(function (obj, name) { + // @ts-ignore + obj[name] = instance[name]; + return obj; + }, {}); +} +/** + * Transform a class constructor into an alpine constructor function. + * + * @param component The class constructor + */ +export function makeAlpineConstructor(component) { + return function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return transformToAlpineData(new (component.bind.apply(component, __spreadArray([void 0], args, false)))()); + }; +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU3RvcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvU3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBSUEsT0FBTyxFQUFDLGVBQWUsRUFBa0MsTUFBTSxhQUFhLENBQUM7QUFFN0U7Ozs7R0FJRztBQUNILElBQU0sYUFBYSxHQUFHLENBQUMsVUFBVSxFQUFFLFdBQVcsRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTTtJQUMxRixPQUFPLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsVUFBVSxFQUFFLFVBQVUsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsTUFBTTtJQUN0RyxNQUFNLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxVQUFVLEVBQUUsTUFBTTtJQUNwRyxJQUFJLEVBQUUsWUFBWSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsS0FBSztJQUNwRyxNQUFNLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRyxXQUFXLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxRQUFRO0lBQ3BHLE1BQU0sRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxPQUFPO0lBQ25HLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQztBQXFCbEI7Ozs7R0FJRztBQUNILElBQUssd0JBS0o7QUFMRCxXQUFLLHdCQUF3QjtJQUM1Qiw2SEFBK0IsQ0FBQTtJQUMv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 \ No newline at end of file diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..6cbf7c1 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,13 @@ +/** + * Export functions and types. + */ +export { AlpineComponent } from './Component'; +export { ComponentStore, transformToAlpineData, makeAlpineConstructor } from './Store'; +export * as Globals from './Global'; +export { AlpineComponents, componentsPlugin } from './Plugin'; +/** + * Alpine plugin as default export. + */ +import { componentsPlugin } from './Plugin'; +export default componentsPlugin; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxPQUFPLEVBUU4sZUFBZSxFQUNmLE1BQU0sYUFBYSxDQUFDO0FBRXJCLE9BQU8sRUFFTixjQUFjLEVBQ2QscUJBQXFCLEVBQ3JCLHFCQUFxQixFQUNyQixNQUFNLFNBQVMsQ0FBQztBQUVqQixPQUFPLEtBQUssT0FBTyxNQUFNLFVBQVUsQ0FBQztBQUVwQyxPQUFPLEVBQ04sZ0JBQWdCLEVBQ2hCLGdCQUFnQixFQUNoQixNQUFNLFVBQVUsQ0FBQztBQUdsQjs7R0FFRztBQUNILE9BQU8sRUFBQyxnQkFBZ0IsRUFBQyxNQUFNLFVBQVUsQ0FBQztBQUMxQyxlQUFlLGdCQUFnQixDQUFDIn0= \ No newline at end of file diff --git a/src/Component.ts b/src/Component.ts deleted file mode 100644 index fab0602..0000000 --- a/src/Component.ts +++ /dev/null @@ -1,161 +0,0 @@ -/** - * Type definition for known class constructors. - * - * @public - */ -export type KnownClassConstructor = new (...args: any[]) => T; -/** - * Type definition for known generic constructors. - * - * @public - */ -export type KnownGenericConstructor = (...args: any[]) => T; -/** - * Type definition for supported constructor functions. - * - * @public - */ -export type KnownConstructor = KnownGenericConstructor| - // @ts-expect-error TS2344 - KnownClassConstructor; - -/** - * Type definition for alpine component constructors. - * - * @public - */ -export type AlpineComponentConstructor = (...args: any[]) => any; - -/** - * Copied from @types/alpinejs because it isn't exported. - * - * {@link https://www.npmjs.com/package/@types/alpinejs} - * - * @public - */ -export declare interface AlpineDataContext { - /** - * Will be executed before Alpine initializes teh rest of the component. - */ - init?(): void; - [stateKey: string]: any; -} - -/** - * Copied from @types/alpinejs because it isn't exported. - * - * {@link https://www.npmjs.com/package/@types/alpinejs} - * - * @public - */ -export declare type AlpineData = AlpineDataContext | string | number | boolean; - -/** - * Type used to define properties that will exist on an x-bind object at runtime. - * - * @template T The component type - * @template Keys The properties to expose to the context (defaults to everything - * accessible with `keyof`) - * @template HiddenKeys Define accessible properties (protected/private) that are - * not included by `keyof` - */ -export type AlpineBindingContext< - T extends AlpineComponent, - Keys extends keyof T = keyof T, - HiddenKeys extends string = '' -> = Record | (Pick & { - [K in HiddenKeys]: string | number | boolean; -}); - -/** - * Light-weight interface for class based components. - * - * Provides property declarations for Alpine magics that will exist when - * used as an Alpine component. - * - * Property declarations copied from @types/alpinejs. - * - * {@link https://www.npmjs.com/package/@types/alpinejs} - * - * @public - */ -export abstract class AlpineComponent implements AlpineDataContext { - - /** - * Access to current Alpine data. - */ - declare $data: this; - - /** - * Retrieve the current DOM node. - */ - declare $el: HTMLElement; - - /** - * Retrieve DOM elements marked with x-ref inside the component. - */ - declare $refs: Record; - - /** - * Access registered global Alpine stores. - */ - declare $store: AlpineData; - - /** - * Dispatch browser events. - * - * @param event the event name - * @param data an event-dependent value associated with the event, the value is then available to the handler using the CustomEvent.detail property - */ - declare $dispatch: (event: string, data?: any) => void; - - /** - * Generate an element's ID and ensure that it won't conflict with other IDs of the same name on the same page. - * - * @param name the name of the id - * @param key suffix on the end of the generated ID, usually helpful for the purpose of identifying id in a loop - */ - declare $id: (name: string, key?: number | string) => string; - - /** - * Execute a given expression AFTER Alpine has made its reactive DOM updates. - * - * @param callback a callback that will be fired after Alpine finishes updating the DOM - */ - declare $nextTick: (callback?: () => void) => Promise; - - /** - * Fire the given callback when the value in the property is changed. - * - * @param property the component property - * @param callback a callback that will fire when a given property is changed - */ - declare $watch: ( - property: K, - callback: (newValue: V, oldValue: V) => void, - ) => void; - - /** - * Declare an object as an x-bind property for this component. - * - * Use this method to define properties for use with x-bind: - * ```typescript - * protected myBinding = this.binding({ - * ["@click.prevent"]() { console.log("click prevented!") } - * }); - * ``` - * - * @protected - * - * @template HiddenKeys Define accessible properties (protected/private) - * that are not included by `keyof` - * - * @param obj The object for use with x-bind - * - * @return The same object passed to {@link obj} - */ - protected binding(obj: AlpineBindingContext) { - return obj; - } - -} diff --git a/src/Global.ts b/src/Global.ts deleted file mode 100644 index 0b6c4bf..0000000 --- a/src/Global.ts +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Type declarations for Alpine and browser window global. - */ -import type {Alpine as AlpineType} from 'alpinejs'; -import type {ComponentStore} from './Store'; -import type {AlpineComponentConstructor} from './Component'; - -/** - * Define the properties we add to the `window.Alpine` object. - * - * @public - */ -export declare interface AlpineComponentMixins { - Components: ComponentStore; - component: (name: string) => AlpineComponentConstructor; -} - -/** - * Expose the properties we add to the `window.Alpine` object. - * - * @public - */ -export declare type AlpineWithComponents = AlpineType&AlpineComponentMixins; - -/** - * Expose the properties we add to the `window.Alpine` object. - * - * @public - */ -export declare type Alpine = AlpineType|AlpineWithComponents; - -/** - * Check if an {@link Alpine} object has the components properties. - * - * @public - * - * @param obj The Alpine object to check - * - * @return True if component properties are injected, false otherwise. - */ -export function satisfiesAlpineWithComponents(obj: Alpine): boolean { - // @ts-ignore - return !!(obj.Components && obj.component); -} - -/** - * Cast an {@link Alpine} object to {@link AlpineWithComponents} if it - * has the injected properties. - * - * @public - * - * @param obj The Alpine object to cast - * - * @return The object cast to {@link AlpineWithComponents} if properties are - * injected, null otherwise. - */ -export function castToAlpineWithComponents(obj: Alpine = window.Alpine): AlpineWithComponents|null { - return satisfiesAlpineWithComponents(obj) ? obj : null; -} - -declare global { - - /** - * Expose window.Alpine and window.AlpineComponents globals. - * - * @public - */ - interface Window { - Alpine: AlpineWithComponents; - AlpineComponents: ComponentStore; - } - -} diff --git a/src/Plugin.ts b/src/Plugin.ts deleted file mode 100644 index 6bc6947..0000000 --- a/src/Plugin.ts +++ /dev/null @@ -1,109 +0,0 @@ -import type Alpine from 'alpinejs'; -import type * as Globals from './Global'; -import { - type ComponentList, - ComponentStore -} from './Store'; - -export namespace AlpineComponents { - - /** - * Bootstrap options. - * - * @public - */ - export interface Options { - /** - * List of named components to register. - */ - components: ComponentList, - - /** - * Create Alpine object and inject into window.Alpine? - */ - bootstrapAlpine: boolean; - /** - * Call Alpine.start()? - */ - startAlpine: boolean; - - /** - * Log errors to console? - */ - logErrors: boolean; - } - - /** - * Default bootstrap options. - * - * Assumes production environment. - * - * @public - */ - export const defaultOptions: Options = { - components: {}, - - bootstrapAlpine: false, - startAlpine: true, - - logErrors: false - }; - - /** - * Bootstrap the components package. - * - * @public - * - * @param options Provided options (defaults applied to missing values {@link defaultOptions}.) - * @param alpine The Alpine instance to use (defaults to window.Alpine or creates Alpine when - * the bootstrapAlpine option is set) - */ - export function bootstrap( - options: Partial = defaultOptions, - alpine: typeof Alpine = window.Alpine - ): void { - const opts: Options = { - ...defaultOptions, - ...options - }; - - if (opts.bootstrapAlpine && alpine !== undefined) { - if (opts.logErrors) { - console.error('Cannot bootstrap Alpine when window.Alpine is already defined.'); - } - return; - } - - Promise.resolve( - opts.bootstrapAlpine ? - import('alpinejs').then((imp) => imp.default) : alpine - ).then((alpine: typeof Alpine): void => { - if (opts.bootstrapAlpine) { - window.Alpine = alpine; - } - - window.AlpineComponents = new ComponentStore(alpine, opts.components, opts.logErrors); - - if (opts.startAlpine) { - alpine.start(); - } - }); - } - -} - -/** - * Export a function to be used with `Alpine.plugin()`. - * - * @public - * - * Calls {@link AlpineComponents.bootstrap} with sensible options. - * - * @param alpine - */ -export function componentsPlugin(alpine: Globals.Alpine) { - AlpineComponents.bootstrap({ - bootstrapAlpine: false, - startAlpine: false - }, alpine); -} diff --git a/src/Store.ts b/src/Store.ts deleted file mode 100644 index 7e62333..0000000 --- a/src/Store.ts +++ /dev/null @@ -1,256 +0,0 @@ -import type Alpine from 'alpinejs'; -import type * as Impl from './Component'; -import type * as Globals from './Global'; - -import {AlpineComponent, type AlpineComponentConstructor} from './Component'; - -/** - * @see https://www.w3schools.com/js/js_reserved.asp - * - * @internal - */ -const ReservedNames = ['abstract', 'arguments', 'await', 'boolean', 'break', 'byte', 'case', - 'catch', 'char', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'double', 'else', - 'enum', 'eval', 'export', 'extends', 'false', 'final', 'finally', 'float', 'for', 'function', 'goto', - 'if', 'implements', 'import', 'in', 'instanceof', 'int', 'interface', 'let', 'long', 'native', 'new', - 'null', 'package', 'private', 'protected', 'public', 'return', 'short', 'static', 'super', 'switch', - 'this', 'throw', 'throws', 'transient', 'true', 'try', 'typeof', 'var', 'void', 'volatile', 'while', - 'with', 'yield']; - -/** - * Type definition for list of named component constructors. - * - * @public - */ -export type ComponentList = { - [name: string]: Impl.KnownConstructor -}; - -/** - * Internal type for component registration. - * - * @internal - */ -type ComponentConstructorData = { - name: string, - constructor: Impl.AlpineComponentConstructor -}; - -/** - * Internal component registration failure reasons. - * - * @internal - */ -enum RegisterComponentFailure { - GenericMustHaveFunctionAsSecond, - NameMustBeProvidedForComponentWithNoDefault, - UnknownArgumentTypes, - ReservedName -} - -export class ComponentStore { - private initialized: boolean = false; - - private alpine: Globals.AlpineWithComponents; - - private components: Record = {}; - - constructor( - alpinejs: typeof Alpine, - components: ComponentList = {}, - private readonly logErrors: boolean = false - ) { - this.alpine = alpinejs; - this.alpine.Components = this; - this.alpine.component = this.component; - - Object.entries(components).forEach(([name, component]): void => { - this.register(name, component); - }); - - window.addEventListener('alpine:init', (): void => { - this.init(); - }); - } - - private init(): void { - if (this.initialized) { - return; - } - - document.dispatchEvent(new CustomEvent('alpine-components:init')); - - Object.entries(this.components) - .forEach(([name]) => - this.registerConstructorAsAlpineData(name)); - - this.initialized = true; - } - - /** - * Retrieve a registered component constructor. - * - * @param name The component name - * - * @return ComponentConstructor - * - * If registered, returns a callable that accepts the component constructor arguments - * and creates the component object. Returns undefined if not registered. - */ - component(name: string): Impl.AlpineComponentConstructor { - // @ts-ignore - return this.components[name]; - } - - registerAll(components: ComponentList): void { - Object.entries(components) - .forEach(([name, component]) => - this.register(name, component)); - } - - /** - * Register a generic object (alpine data) as a component. - * - * @param name The name of the component (registered to alpine for use with x-data.) - * @param component The function that returns component data. - */ - register(name: string, component: Impl.KnownConstructor): void; - - /** - * Register a class inheriting from {@link Impl.AlpineComponent} as a component. - * - * @param component The name/symbol of the class to register as a component. - * @param name The name of the component (registered to alpine for use with x-data.) - */ - register(component: Impl.KnownClassConstructor, name?: string): void; - - register( - // @ts-expect-error TS3244 - nameOrComponentClass: string|Impl.KnownConstructor|Impl.KnownClassConstructor, - constructorOrComponentName: Impl.KnownConstructor|string = '' - ): void { - let component: ComponentConstructorData; - - if (typeof nameOrComponentClass === 'string') { // register generic object (normal alpine data) - if (typeof constructorOrComponentName === 'string') { - this.logRegisterFailure(RegisterComponentFailure.GenericMustHaveFunctionAsSecond); - return; - } - component = ComponentStore.getObjectData(nameOrComponentClass, constructorOrComponentName); - } else if (typeof nameOrComponentClass === 'function') { // register class as component - component = ComponentStore.getClassData(>nameOrComponentClass, constructorOrComponentName); - if (component.name === "") { - this.logRegisterFailure(RegisterComponentFailure.NameMustBeProvidedForComponentWithNoDefault); - } - } else { - this.logRegisterFailure(RegisterComponentFailure.UnknownArgumentTypes); - return; - } - - if (ReservedNames.includes(component.name)) { - this.logRegisterFailure(RegisterComponentFailure.ReservedName); - } - - this.components[component.name] = component.constructor; - - if (this.initialized) { - this.registerConstructorAsAlpineData(component.name); - } - } - - /** - * Register a component to Alpine through Alpine.data(). - * - * @param name The name of the component (must already be registered to the store.) - */ - private registerConstructorAsAlpineData(name: string): void { - this.alpine.data(name, this.component(name)); - } - - private static getObjectData( - name: string, - component: Impl.KnownConstructor - ): ComponentConstructorData { - return { - name: name, - constructor: ((component.prototype instanceof AlpineComponent) ? - // @ts-ignore - makeAlpineConstructor(component) : component) - }; - } - - private static getClassData( - component: Impl.KnownClassConstructor, - name?: string - ): ComponentConstructorData { - const resolvedName: string = (name !== undefined ? name : component.prototype.name); - - return { - name: resolvedName, - constructor: makeAlpineConstructor(component) - }; - } - - private logRegisterFailure(reason: RegisterComponentFailure): void { - if (!this.logErrors) { - return; - } - - switch (reason) { - case RegisterComponentFailure.GenericMustHaveFunctionAsSecond: - console.error(`Second argument must be a constructor function for component.`); - break; - case RegisterComponentFailure.NameMustBeProvidedForComponentWithNoDefault: - // should be impossible because we fall back to prototype name - console.error(`Component name must be provided when class doesn't specify a default.`); - break; - case RegisterComponentFailure.UnknownArgumentTypes: - console.error(`Cannot register component with provided argument types. Check Typescript definitions for usage.`); - break; - case RegisterComponentFailure.ReservedName: - console.error(`Cannot register component with name matching a reserved keyword.`); - break; - } - } - -} - -/** - * Copy prototype functions and object properties to an empty object. - * - * @param instance The object to copy functions and properties from - */ -export function transformToAlpineData(instance: T): object { - let methodNames: string[] = []; - for ( - let prototype = Object.getPrototypeOf(instance); - prototype.constructor.name !== 'Object'; - prototype = Object.getPrototypeOf(prototype) - ) { - Object.getOwnPropertyNames(prototype).forEach((name: string): void => { - if (methodNames.includes(name)) { - return; - } - methodNames.push(name); - }); - } - - return [ - ...methodNames, // methods - ...Object.getOwnPropertyNames(instance) // properties - ].reduce((obj: {}, name: string) => { - // @ts-ignore - obj[name] = instance[name]; - - return obj; - }, {}); -} - -/** - * Transform a class constructor into an alpine constructor function. - * - * @param component The class constructor - */ -export function makeAlpineConstructor(component: Impl.KnownClassConstructor): Impl.AlpineComponentConstructor { - return (...args: any[]) => transformToAlpineData(new component(...args)); -} diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index ca546e5..0000000 --- a/src/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Export functions and types. - */ - -export { - type AlpineBindingContext, - type AlpineComponentConstructor, - type AlpineData, - type AlpineDataContext, - type KnownConstructor, - type KnownClassConstructor, - type KnownGenericConstructor, - AlpineComponent -} from './Component'; - -export { - type ComponentList, - ComponentStore, - transformToAlpineData, - makeAlpineConstructor -} from './Store'; - -export * as Globals from './Global'; - -export { - AlpineComponents, - componentsPlugin -} from './Plugin'; - - -/** - * Alpine plugin as default export. - */ -import {componentsPlugin} from './Plugin'; -export default componentsPlugin; diff --git a/types/Component.d.ts b/types/Component.d.ts new file mode 100644 index 0000000..8f6ce9d --- /dev/null +++ b/types/Component.d.ts @@ -0,0 +1,135 @@ +/** + * Type definition for known class constructors. + * + * @public + */ +export type KnownClassConstructor = new (...args: any[]) => T; +/** + * Type definition for known generic constructors. + * + * @public + */ +export type KnownGenericConstructor = (...args: any[]) => T; +/** + * Type definition for supported constructor functions. + * + * @public + */ +export type KnownConstructor = KnownGenericConstructor | KnownClassConstructor; +/** + * Type definition for alpine component constructors. + * + * @public + */ +export type AlpineComponentConstructor = (...args: any[]) => any; +/** + * Copied from @types/alpinejs because it isn't exported. + * + * {@link https://www.npmjs.com/package/@types/alpinejs} + * + * @public + */ +export declare interface AlpineDataContext { + /** + * Will be executed before Alpine initializes teh rest of the component. + */ + init?(): void; + [stateKey: string]: any; +} +/** + * Copied from @types/alpinejs because it isn't exported. + * + * {@link https://www.npmjs.com/package/@types/alpinejs} + * + * @public + */ +export declare type AlpineData = AlpineDataContext | string | number | boolean; +/** + * Type used to define properties that will exist on an x-bind object at runtime. + * + * @template T The component type + * @template Keys The properties to expose to the context (defaults to everything + * accessible with `keyof`) + * @template HiddenKeys Define accessible properties (protected/private) that are + * not included by `keyof` + */ +export type AlpineBindingContext = Record | (Pick & { + [K in HiddenKeys]: string | number | boolean; +}); +/** + * Light-weight interface for class based components. + * + * Provides property declarations for Alpine magics that will exist when + * used as an Alpine component. + * + * Property declarations copied from @types/alpinejs. + * + * {@link https://www.npmjs.com/package/@types/alpinejs} + * + * @public + */ +export declare abstract class AlpineComponent implements AlpineDataContext { + /** + * Access to current Alpine data. + */ + $data: this; + /** + * Retrieve the current DOM node. + */ + $el: HTMLElement; + /** + * Retrieve DOM elements marked with x-ref inside the component. + */ + $refs: Record; + /** + * Access registered global Alpine stores. + */ + $store: AlpineData; + /** + * Dispatch browser events. + * + * @param event the event name + * @param data an event-dependent value associated with the event, the value is then available to the handler using the CustomEvent.detail property + */ + $dispatch: (event: string, data?: any) => void; + /** + * Generate an element's ID and ensure that it won't conflict with other IDs of the same name on the same page. + * + * @param name the name of the id + * @param key suffix on the end of the generated ID, usually helpful for the purpose of identifying id in a loop + */ + $id: (name: string, key?: number | string) => string; + /** + * Execute a given expression AFTER Alpine has made its reactive DOM updates. + * + * @param callback a callback that will be fired after Alpine finishes updating the DOM + */ + $nextTick: (callback?: () => void) => Promise; + /** + * Fire the given callback when the value in the property is changed. + * + * @param property the component property + * @param callback a callback that will fire when a given property is changed + */ + $watch: (property: K, callback: (newValue: V, oldValue: V) => void) => void; + /** + * Declare an object as an x-bind property for this component. + * + * Use this method to define properties for use with x-bind: + * ```typescript + * protected myBinding = this.binding({ + * ["@click.prevent"]() { console.log("click prevented!") } + * }); + * ``` + * + * @protected + * + * @template HiddenKeys Define accessible properties (protected/private) + * that are not included by `keyof` + * + * @param obj The object for use with x-bind + * + * @return The same object passed to {@link obj} + */ + protected binding(obj: AlpineBindingContext): AlpineBindingContext; +} diff --git a/types/Global.d.ts b/types/Global.d.ts new file mode 100644 index 0000000..973da58 --- /dev/null +++ b/types/Global.d.ts @@ -0,0 +1,60 @@ +/** + * Type declarations for Alpine and browser window global. + */ +import type { Alpine as AlpineType } from 'alpinejs'; +import type { ComponentStore } from './Store'; +import type { AlpineComponentConstructor } from './Component'; +/** + * Define the properties we add to the `window.Alpine` object. + * + * @public + */ +export declare interface AlpineComponentMixins { + Components: ComponentStore; + component: (name: string) => AlpineComponentConstructor; +} +/** + * Expose the properties we add to the `window.Alpine` object. + * + * @public + */ +export declare type AlpineWithComponents = AlpineType & AlpineComponentMixins; +/** + * Expose the properties we add to the `window.Alpine` object. + * + * @public + */ +export declare type Alpine = AlpineType | AlpineWithComponents; +/** + * Check if an {@link Alpine} object has the components properties. + * + * @public + * + * @param obj The Alpine object to check + * + * @return True if component properties are injected, false otherwise. + */ +export declare function satisfiesAlpineWithComponents(obj: Alpine): boolean; +/** + * Cast an {@link Alpine} object to {@link AlpineWithComponents} if it + * has the injected properties. + * + * @public + * + * @param obj The Alpine object to cast + * + * @return The object cast to {@link AlpineWithComponents} if properties are + * injected, null otherwise. + */ +export declare function castToAlpineWithComponents(obj?: Alpine): AlpineWithComponents | null; +declare global { + /** + * Expose window.Alpine and window.AlpineComponents globals. + * + * @public + */ + interface Window { + Alpine: AlpineWithComponents; + AlpineComponents: ComponentStore; + } +} diff --git a/types/Plugin.d.ts b/types/Plugin.d.ts new file mode 100644 index 0000000..0a83d57 --- /dev/null +++ b/types/Plugin.d.ts @@ -0,0 +1,56 @@ +import type Alpine from 'alpinejs'; +import type * as Globals from './Global'; +import { type ComponentList } from './Store'; +export declare namespace AlpineComponents { + /** + * Bootstrap options. + * + * @public + */ + interface Options { + /** + * List of named components to register. + */ + components: ComponentList; + /** + * Create Alpine object and inject into window.Alpine? + */ + bootstrapAlpine: boolean; + /** + * Call Alpine.start()? + */ + startAlpine: boolean; + /** + * Log errors to console? + */ + logErrors: boolean; + } + /** + * Default bootstrap options. + * + * Assumes production environment. + * + * @public + */ + const defaultOptions: Options; + /** + * Bootstrap the components package. + * + * @public + * + * @param options Provided options (defaults applied to missing values {@link defaultOptions}.) + * @param alpine The Alpine instance to use (defaults to window.Alpine or creates Alpine when + * the bootstrapAlpine option is set) + */ + function bootstrap(options?: Partial, alpine?: typeof Alpine): void; +} +/** + * Export a function to be used with `Alpine.plugin()`. + * + * @public + * + * Calls {@link AlpineComponents.bootstrap} with sensible options. + * + * @param alpine + */ +export declare function componentsPlugin(alpine: Globals.Alpine): void; diff --git a/types/Store.d.ts b/types/Store.d.ts new file mode 100644 index 0000000..695e678 --- /dev/null +++ b/types/Store.d.ts @@ -0,0 +1,66 @@ +import type Alpine from 'alpinejs'; +import type * as Impl from './Component'; +import { AlpineComponent } from './Component'; +/** + * Type definition for list of named component constructors. + * + * @public + */ +export type ComponentList = { + [name: string]: Impl.KnownConstructor; +}; +export declare class ComponentStore { + private readonly logErrors; + private initialized; + private alpine; + private components; + constructor(alpinejs: typeof Alpine, components?: ComponentList, logErrors?: boolean); + private init; + /** + * Retrieve a registered component constructor. + * + * @param name The component name + * + * @return ComponentConstructor + * + * If registered, returns a callable that accepts the component constructor arguments + * and creates the component object. Returns undefined if not registered. + */ + component(name: string): Impl.AlpineComponentConstructor; + registerAll(components: ComponentList): void; + /** + * Register a generic object (alpine data) as a component. + * + * @param name The name of the component (registered to alpine for use with x-data.) + * @param component The function that returns component data. + */ + register(name: string, component: Impl.KnownConstructor): void; + /** + * Register a class inheriting from {@link Impl.AlpineComponent} as a component. + * + * @param component The name/symbol of the class to register as a component. + * @param name The name of the component (registered to alpine for use with x-data.) + */ + register(component: Impl.KnownClassConstructor, name?: string): void; + /** + * Register a component to Alpine through Alpine.data(). + * + * @param name The name of the component (must already be registered to the store.) + */ + private registerConstructorAsAlpineData; + private static getObjectData; + private static getClassData; + private logRegisterFailure; +} +/** + * Copy prototype functions and object properties to an empty object. + * + * @param instance The object to copy functions and properties from + */ +export declare function transformToAlpineData(instance: T): object; +/** + * Transform a class constructor into an alpine constructor function. + * + * @param component The class constructor + */ +export declare function makeAlpineConstructor(component: Impl.KnownClassConstructor): Impl.AlpineComponentConstructor; diff --git a/types/index.d.ts b/types/index.d.ts new file mode 100644 index 0000000..e4004f6 --- /dev/null +++ b/types/index.d.ts @@ -0,0 +1,12 @@ +/** + * Export functions and types. + */ +export { type AlpineBindingContext, type AlpineComponentConstructor, type AlpineData, type AlpineDataContext, type KnownConstructor, type KnownClassConstructor, type KnownGenericConstructor, AlpineComponent } from './Component'; +export { type ComponentList, ComponentStore, transformToAlpineData, makeAlpineConstructor } from './Store'; +export * as Globals from './Global'; +export { AlpineComponents, componentsPlugin } from './Plugin'; +/** + * Alpine plugin as default export. + */ +import { componentsPlugin } from './Plugin'; +export default componentsPlugin;