adonisjs-livewire
Version:
A front-end framework for AdonisJS
228 lines (222 loc) • 10.9 kB
JavaScript
import fs from 'node:fs';
import { SupportLazyLoading } from '../src/features/support_lazy_loading/support_lazy_loading.js';
import edge from 'edge.js';
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { SupportDecorators } from '../src/features/support_decorators/support_decorators.js';
import { SupportEvents } from '../src/features/support_events/support_events.js';
import { SupportJsEvaluation } from '../src/features/support_js_valuation/support_js_evaluation.js';
import { SupportRedirects } from '../src/features/support_redirects/support_redirects.js';
import { SupportScriptsAndAssets } from '../src/features/support_scripts_and_assets/support_scripts_and_assets.js';
import { SupportAutoInjectedAssets } from '../src/features/support_auto_injected_assets/support_auto_injected_assets.js';
import { defaultConfig } from '../src/define_config.js';
import { EventBus } from '../src/event_bus.js';
import { ModelSynth } from '../src/synthesizers/model.js';
import { ArraySynth } from '../src/synthesizers/array.js';
const currentDirname = dirname(fileURLToPath(import.meta.url));
const packageJson = JSON.parse(fs.readFileSync(`${currentDirname}/../../package.json`, 'utf-8'));
export default class LivewireProvider {
app;
constructor(app) {
this.app = app;
}
async boot() {
let livewireJs = fs
.readFileSync(`${currentDirname}/../assets/livewire.js`, 'utf-8')
.replace('_token', '_csrf');
const app = await this.app.container.make('app');
const router = await this.app.container.make('router');
this.app.container.singleton(EventBus, () => new EventBus());
app.config.set('app.http.generateRequestId', true);
app.config.set('app.http.useAsyncLocalStorage', true);
const Livewire = await import('../src/livewire.js').then((m) => m.default);
const LivewireTag = await import('../src/livewire_tag.js').then((m) => m.default);
const config = this.app.config.get('livewire', defaultConfig);
const livewire = new Livewire(app, config);
this.app.container.singleton('livewire', () => {
return livewire;
});
edge.global('livewire', livewire);
edge.registerTag(new LivewireTag());
edge.registerTag({
tagName: 'livewireStyles',
block: false,
seekable: false,
compile(_parser, buffer, _token) {
buffer.outputRaw(`<link rel="stylesheet" href="/livewire.css?v=${packageJson.version}">`);
},
});
edge.registerTag({
tagName: 'livewireScripts',
block: false,
seekable: false,
compile(_parser, buffer, token) {
buffer.outputExpression('`<script src="/livewire.js?v=' +
packageJson.version +
'" data-csrf="${state.request.csrfToken ?? \'\'}" data-update-uri="/livewire/update" data-navigate-once="true"></script>`', token.filename, token.loc.start.line, false);
},
});
let regex = /<livewire:([a-zA-Z0-9\.\-]+)([^>]*)\/>/g;
edge.processor.process('raw', (value) => {
let raw = value.raw;
// raw = raw.replace("<livewire:styles/>", "@livewireStyles")
// raw = raw.replace("<livewire:styles />", "@livewireStyles")
// raw = raw.replace("<livewire:scripts/>", "@livewireScripts")
// raw = raw.replace("<livewire:scripts />", "@livewireScripts")
let matches = raw.match(regex);
if (!matches) {
return;
}
for (const match of matches) {
let [_, component, props] = match.match(/<livewire:([a-zA-Z0-9\.\-:.]+)([^>]*)\/>/) || [];
let attributes = {};
let options = {};
if (props) {
let regex = /(@|:|wire:)?([a-zA-Z0-9\-:.]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
let matches = props.match(regex);
let propsRemainder = props;
if (matches) {
for (const match of matches) {
let [m, prefix, key, value] = match.match(/(@|:|wire:)?([a-zA-Z0-9\-:.]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/) || [];
if (prefix === ':' && key !== 'is' && key !== 'component') {
attributes[key] = `_____${value}_____`;
}
else if (prefix === 'wire:' && key === 'key') {
options.key = `_____${value}_____`;
}
else if (prefix === 'wire:') {
if (key === 'model') {
attributes[`wire:${key}`] = '$parent.' + value;
}
else {
attributes[`wire:${key}`] = value;
}
}
else {
let curlyMatch = value.match(/(\\)?{{(.*?)}}/);
if (curlyMatch) {
attributes[`${prefix ?? ''}${key}`] =
`_____\`${value.replace(/\{\{\s*([^}]+)\s*\}\}/g, '${$1}')}\`_____`;
}
else {
attributes[`${prefix ?? ''}${key}`] = value;
}
}
if (m) {
propsRemainder = propsRemainder.replace(m, '');
}
}
}
propsRemainder
.split(' ')
.map((prop) => prop.trim())
.filter(Boolean)
.forEach((prop) => {
attributes[prop] = true;
});
}
if (component === 'dynamic-component' || component === 'is') {
component = attributes['component'] ?? attributes['is'];
delete attributes['component'];
delete attributes['is'];
}
else {
component = `'${component}'`;
}
const attrs = JSON.stringify(attributes).replace(/"_____([^"]*)_____"/g, '$1');
const opts = JSON.stringify(options).replace(/"_____([^"]*)_____"/g, '$1');
raw = raw.replace(match, `@livewire(${component}, ${attrs}, ${opts})`);
}
return raw;
});
router.get('/livewire.css', async ({ response }) => {
response.type('text/css');
response.header('Cache-Control', 'public, max-age=31536000');
let progressBarColor = config.navigate.progressBarColor;
return `[wire\:loading][wire\:loading], [wire\:loading\.delay][wire\:loading\.delay], [wire\:loading\.inline-block][wire\:loading\.inline-block], [wire\:loading\.inline][wire\:loading\.inline], [wire\:loading\.block][wire\:loading\.block], [wire\:loading\.flex][wire\:loading\.flex], [wire\:loading\.table][wire\:loading\.table], [wire\:loading\.grid][wire\:loading\.grid], [wire\:loading\.inline-flex][wire\:loading\.inline-flex] {
display: none;
}
[wire\:loading\.delay\.none][wire\:loading\.delay\.none], [wire\:loading\.delay\.shortest][wire\:loading\.delay\.shortest], [wire\:loading\.delay\.shorter][wire\:loading\.delay\.shorter], [wire\:loading\.delay\.short][wire\:loading\.delay\.short], [wire\:loading\.delay\.default][wire\:loading\.delay\.default], [wire\:loading\.delay\.long][wire\:loading\.delay\.long], [wire\:loading\.delay\.longer][wire\:loading\.delay\.longer], [wire\:loading\.delay\.longest][wire\:loading\.delay\.longest] {
display: none;
}
[wire\:offline][wire\:offline] {
display: none;
}
[wire\:dirty]:not(textarea):not(input):not(select) {
display: none;
}
:root {
--livewire-progress-bar-color: ${progressBarColor};
}
[x-cloak] {
display: none !important;
}`;
});
router.get('/livewire.js', async ({ response }) => {
response.type('text/javascript');
response.header('Cache-Control', 'public, max-age=31536000');
return livewireJs;
});
router.livewire = (pattern, component, params = {}) => {
return router.get(pattern, async ({ view, request }) => {
component = component || pattern;
component = component.replace(/^\//, '');
component = component.replace(/\/$/, '');
component = component.replace(/\//g, '.');
let parameters = {
...request.params(),
...params,
};
return await livewire.mount(component, parameters, { layout: { name: config.layout } });
});
};
router.post('/livewire/update', async (ctx) => {
let components = ctx.request.input('components', []);
let result = {
components: [],
assets: [],
};
let isRedirect = false;
for (const component of components) {
let snapshot = JSON.parse(component.snapshot);
let [newSnapshot, effects] = await livewire.update(snapshot, component.updates, component.calls);
if (effects && effects.redirect) {
isRedirect = true;
}
result.components.push({
snapshot: JSON.stringify(newSnapshot),
effects,
});
}
// @ts-ignore
if (ctx.session && !isRedirect) {
// @ts-ignore
ctx.session.responseFlashMessages.clear();
// @ts-ignore
ctx.session.flashMessages.clear();
}
return result;
});
}
async register() {
const Livewire = await import('../src/livewire.js').then((m) => m.default);
const FEATURES = [
SupportDecorators,
SupportEvents,
SupportJsEvaluation,
SupportRedirects,
SupportScriptsAndAssets,
SupportAutoInjectedAssets,
SupportLazyLoading,
];
for (const feature of FEATURES) {
Livewire.componentHook(feature);
//@ts-ignore
if (feature['provide']) {
//@ts-ignore
await feature['provide'](this.app);
}
}
Livewire.registerPropertySynthesizer([ModelSynth, ArraySynth]);
}
}