xpine 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +0 -1
- package/dist/src/build/dev-server.js +10 -0
- package/dist/src/build/spa.js +152 -0
- package/dist/src/scripts/xpine-build.js +0 -1
- package/dist/src/scripts/xpine-dev.js +0 -1
- package/dist/src/static/dev-server.js +0 -1
- package/dist/src/static/spa.js +0 -1
- package/package.json +1 -1
- package/src/auth.ts +0 -40
- package/src/build/typescript-builder.ts +0 -180
- package/src/express.ts +0 -120
- package/src/runDevServer.ts +0 -64
- package/src/scripts/build-module.ts +0 -36
- package/src/scripts/build.ts +0 -274
- package/src/scripts/xpine-build.ts +0 -5
- package/src/scripts/xpine-dev.ts +0 -5
- package/src/static/dev-server.mjs +0 -9
- package/src/static/spa.mjs +0 -175
- package/src/util/env.ts +0 -7
- package/src/util/get-config.ts +0 -65
- package/src/util/html.ts +0 -32
- package/src/util/require.ts +0 -5
package/src/static/spa.mjs
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
// Enables SPA like behavior for links
|
|
2
|
-
|
|
3
|
-
async function replaceDocumentContentsWithLinkResponse() {
|
|
4
|
-
const links = [...document.querySelectorAll('[data-spa]')];
|
|
5
|
-
for (const link of links) {
|
|
6
|
-
link.addEventListener('click', updatePageOnLinkClick);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
async function updatePageOnLinkClick(e) {
|
|
12
|
-
const targetHref = e?.target?.getAttribute('href');
|
|
13
|
-
if (!targetHref) return;
|
|
14
|
-
if (isExternalURL(targetHref) && !e?.target?.getAttribute('data-spa-crossorigin')) return;
|
|
15
|
-
e.preventDefault();
|
|
16
|
-
try {
|
|
17
|
-
await getNewPageContent(targetHref);
|
|
18
|
-
if (targetHref === window.location.pathname) return;
|
|
19
|
-
window.history.pushState(
|
|
20
|
-
{
|
|
21
|
-
type: 'link-click',
|
|
22
|
-
targetHref,
|
|
23
|
-
},
|
|
24
|
-
'',
|
|
25
|
-
targetHref
|
|
26
|
-
);
|
|
27
|
-
const event = new CustomEvent('spa:updatePageURL', {
|
|
28
|
-
detail: {
|
|
29
|
-
href: targetHref,
|
|
30
|
-
},
|
|
31
|
-
});
|
|
32
|
-
window.dispatchEvent(event);
|
|
33
|
-
} catch (err) {
|
|
34
|
-
console.error(err);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async function getNewPageContent(href) {
|
|
39
|
-
try {
|
|
40
|
-
const res = await fetch(href);
|
|
41
|
-
const text = await res.text();
|
|
42
|
-
const parser = new DOMParser();
|
|
43
|
-
const dom = parser.parseFromString(text, 'text/html');
|
|
44
|
-
diffHead(dom);
|
|
45
|
-
const newScripts = removeBodyScripts(dom);
|
|
46
|
-
const clonedPersistentNodes = getPersistentNodesFromDocument(dom);
|
|
47
|
-
const body = dom.body.innerHTML;
|
|
48
|
-
document.body.innerHTML = body;
|
|
49
|
-
for (const clonedNode of clonedPersistentNodes) {
|
|
50
|
-
// @ts-ignore
|
|
51
|
-
const newNode = document.body.querySelector(`[data-persistent="${clonedNode?.getAttribute('data-persistent')}"]`);
|
|
52
|
-
newNode.replaceWith(clonedNode);
|
|
53
|
-
}
|
|
54
|
-
replaceAttributesOnDocumentBody(dom);
|
|
55
|
-
replaceDocumentContentsWithLinkResponse();
|
|
56
|
-
for (const script of newScripts) document.body.appendChild(script);
|
|
57
|
-
// Send an event
|
|
58
|
-
const event = new CustomEvent('spa:updatePageContent', {
|
|
59
|
-
detail: {
|
|
60
|
-
href,
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
window.dispatchEvent(event);
|
|
64
|
-
} catch (err) {
|
|
65
|
-
console.error(err);
|
|
66
|
-
console.error('Error getting link', href);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function diffHead(dom) {
|
|
71
|
-
const domHeadChildren = [...dom.head.children];
|
|
72
|
-
const currentHeadChildren = [...document.head.children];
|
|
73
|
-
const result = {
|
|
74
|
-
childrenToAdd: [],
|
|
75
|
-
childrenToRemove: [],
|
|
76
|
-
};
|
|
77
|
-
for (const domChild of domHeadChildren) {
|
|
78
|
-
const foundInCurrentChildren = currentHeadChildren.find(child => child.outerHTML === domChild.outerHTML);
|
|
79
|
-
if (!foundInCurrentChildren) result.childrenToAdd.push(domChild);
|
|
80
|
-
}
|
|
81
|
-
for (const currentChild of currentHeadChildren) {
|
|
82
|
-
const foundInDomChildren = domHeadChildren.find(child => child.outerHTML === currentChild.outerHTML);
|
|
83
|
-
if (!foundInDomChildren) result.childrenToRemove.push(currentChild);
|
|
84
|
-
}
|
|
85
|
-
applyHeadDiffsToDocument(result);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function applyHeadDiffsToDocument(diff) {
|
|
89
|
-
for (const child of diff.childrenToRemove) {
|
|
90
|
-
// Remove from head
|
|
91
|
-
document.head.removeChild(child);
|
|
92
|
-
}
|
|
93
|
-
for (const child of diff.childrenToAdd) {
|
|
94
|
-
// Handle script tags
|
|
95
|
-
if (child?.nodeName?.toLowerCase() == 'script') {
|
|
96
|
-
const scriptElement = document.createElement('script');
|
|
97
|
-
portAttributesToElement(child, scriptElement);
|
|
98
|
-
scriptElement.innerHTML = child.innerHTML;
|
|
99
|
-
document.head.appendChild(scriptElement);
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
// Add to the head
|
|
103
|
-
document.head.appendChild(child);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function removeBodyScripts(dom) {
|
|
108
|
-
const scripts = [...dom.body.querySelectorAll('script')];
|
|
109
|
-
const newElements = [];
|
|
110
|
-
for (const script of scripts) {
|
|
111
|
-
const element = document.createElement('script');
|
|
112
|
-
portAttributesToElement(script, element);
|
|
113
|
-
element.innerHTML = script.innerHTML;
|
|
114
|
-
dom.body.removeChild(script);
|
|
115
|
-
newElements.push(element);
|
|
116
|
-
}
|
|
117
|
-
return newElements;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function portAttributesToElement(oldEl, newEl) {
|
|
121
|
-
for (const attribute of oldEl.attributes) {
|
|
122
|
-
newEl.setAttribute(attribute.name, attribute.nodeValue);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function getPersistentNodesFromDocument(dom) {
|
|
127
|
-
// Find persistent nodes
|
|
128
|
-
const persistentNodesFromDom = [...dom.querySelectorAll('[data-persistent]')].map(el => el.getAttribute('data-persistent'));
|
|
129
|
-
const persistentNodesFromBody = [...document.body.querySelectorAll('[data-persistent]')];
|
|
130
|
-
// Cross match the persistent nodes between current page and new page to find valid ones
|
|
131
|
-
const validPersistentNodes = persistentNodesFromBody.filter(el => {
|
|
132
|
-
return persistentNodesFromDom.includes(el.getAttribute('data-persistent'));
|
|
133
|
-
});
|
|
134
|
-
// Clone
|
|
135
|
-
return validPersistentNodes.map(el => el.cloneNode(true));
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function replaceAttributesOnDocumentBody(dom) {
|
|
139
|
-
while (document.body.attributes.length > 0) {
|
|
140
|
-
document.body.removeAttribute(document.body.attributes[0].name);
|
|
141
|
-
}
|
|
142
|
-
// Set attributes on the body
|
|
143
|
-
for (const attribute of dom.body.attributes) {
|
|
144
|
-
document.body.setAttribute(attribute.name, attribute.nodeValue);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async function handleBackButton() {
|
|
149
|
-
await getNewPageContent(window.history.state?.targetHref || window.location.href);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function isRelativeURL(url) {
|
|
153
|
-
if (typeof url !== 'string') return false;
|
|
154
|
-
try {
|
|
155
|
-
new URL(url);
|
|
156
|
-
return false;
|
|
157
|
-
} catch (err) {
|
|
158
|
-
if (url?.startsWith('//')) return false;
|
|
159
|
-
return true;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function isExternalURL(url) {
|
|
164
|
-
if (isRelativeURL(url)) return false;
|
|
165
|
-
try {
|
|
166
|
-
const computedURL = new URL(url);
|
|
167
|
-
return computedURL?.origin !== window.location?.origin;
|
|
168
|
-
} catch {
|
|
169
|
-
return false; // Not a URL
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
window.addEventListener('DOMContentLoaded', replaceDocumentContentsWithLinkResponse);
|
|
174
|
-
window.addEventListener('popstate', handleBackButton);
|
|
175
|
-
|
package/src/util/env.ts
DELETED
package/src/util/get-config.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import require from './require';
|
|
3
|
-
import { XPineConfig } from '../../types';
|
|
4
|
-
|
|
5
|
-
const rootDir = process.cwd();
|
|
6
|
-
|
|
7
|
-
export function fromRoot(pathName?: string) {
|
|
8
|
-
if (!pathName) return rootDir;
|
|
9
|
-
return path.join(rootDir, pathName);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const userConfig = require(path.join(process.cwd(), './xpine.config.mjs')).default;
|
|
13
|
-
|
|
14
|
-
const configDefaults = {
|
|
15
|
-
rootDir: fromRoot(),
|
|
16
|
-
srcDir: fromRoot('./src'),
|
|
17
|
-
distDir: fromRoot('./dist'),
|
|
18
|
-
packageJsonPath: fromRoot('package.json'),
|
|
19
|
-
// We need to use getters here in the event someone wants to change folders, such as the dist folder
|
|
20
|
-
get distPublicDir() {
|
|
21
|
-
return path.join(this.distDir, './public');
|
|
22
|
-
},
|
|
23
|
-
get distPublicScriptsDir() {
|
|
24
|
-
return path.join(this.distPublicDir, './scripts');
|
|
25
|
-
},
|
|
26
|
-
get distTempFolder() {
|
|
27
|
-
return path.join(this.distDir, './temp');
|
|
28
|
-
},
|
|
29
|
-
get clientJSBundlePath() {
|
|
30
|
-
return path.join(this.distPublicScriptsDir, './app.js');
|
|
31
|
-
},
|
|
32
|
-
get alpineDataPath() {
|
|
33
|
-
return path.join(this.distTempFolder, './alpine-data.ts');
|
|
34
|
-
},
|
|
35
|
-
get serverDistDir() {
|
|
36
|
-
return path.join(this.distDir, './server');
|
|
37
|
-
},
|
|
38
|
-
get serverDistAppPath() {
|
|
39
|
-
return path.join(this.serverDistDir, './app.js');
|
|
40
|
-
},
|
|
41
|
-
// Important dirs/paths
|
|
42
|
-
get pagesDir() {
|
|
43
|
-
return path.join(this.srcDir, './pages');
|
|
44
|
-
},
|
|
45
|
-
get publicDir() {
|
|
46
|
-
return path.join(this.srcDir, './public');
|
|
47
|
-
},
|
|
48
|
-
get serverDir() {
|
|
49
|
-
return path.join(this.srcDir, './server');
|
|
50
|
-
},
|
|
51
|
-
get runDir() {
|
|
52
|
-
return path.join(this.serverDir, './run');
|
|
53
|
-
},
|
|
54
|
-
get serverAppPath() {
|
|
55
|
-
return path.join(this.serverDir, './app.ts');
|
|
56
|
-
},
|
|
57
|
-
get globalCSSFile() {
|
|
58
|
-
return path.join(this.publicDir, './styles/global.css');
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export const config: XPineConfig = {
|
|
63
|
-
...configDefaults,
|
|
64
|
-
...userConfig,
|
|
65
|
-
};
|
package/src/util/html.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
export class html {
|
|
2
|
-
|
|
3
|
-
static attributeObjectToString(props) {
|
|
4
|
-
if (!props) return '';
|
|
5
|
-
return Object.entries(props)
|
|
6
|
-
.filter(([, value]) => value !== null && value !== undefined)
|
|
7
|
-
.map(([key, value], index) => {
|
|
8
|
-
const start = index === 0 ? ' ' : '';
|
|
9
|
-
return `${start}${key}="${value}"`;
|
|
10
|
-
})
|
|
11
|
-
.join(' ');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
static async fragment(props) {
|
|
15
|
-
const childrenResult = await Promise.all(props.children.flat());
|
|
16
|
-
return childrenResult.filter(Boolean).join('');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
static async createElement(type, props, ...children) {
|
|
20
|
-
const childrenResult = await Promise.all(children.flat());
|
|
21
|
-
// Handle passing in another element
|
|
22
|
-
if (typeof type === 'function') {
|
|
23
|
-
const result = await type({ ...props, children: childrenResult, });
|
|
24
|
-
return result;
|
|
25
|
-
}
|
|
26
|
-
return `<${type}${this.attributeObjectToString(props)}>${childrenResult.filter(Boolean).join('')}</${type}>`;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function JSXRuntime() {
|
|
31
|
-
return true;
|
|
32
|
-
}
|