Use wbpack for website

This commit is contained in:
Andras Schmelczer 2025-07-06 15:16:51 +01:00
parent a2119b0f32
commit f07aa5faa7
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C
13 changed files with 6189 additions and 481 deletions

View file

@ -5,17 +5,17 @@
version: 2 version: 2
updates: updates:
- package-ecosystem: "cargo" - package-ecosystem: 'cargo'
directories: ["**"] directories: ['**']
schedule: schedule:
interval: "daily" interval: 'daily'
- package-ecosystem: "github-actions" - package-ecosystem: 'github-actions'
directories: ["**"] directories: ['**']
schedule: schedule:
interval: "daily" interval: 'daily'
- package-ecosystem: "npm" - package-ecosystem: 'npm'
directories: ["/reconcile-js"] directories: ['/reconcile-js', '/examples/website']
schedule: schedule:
interval: "daily" interval: 'daily'

10
.prettierrc Normal file
View file

@ -0,0 +1,10 @@
{
"trailingComma": "es5",
"printWidth": 90,
"tabWidth": 2,
"singleQuote": true,
"endOfLine": "lf",
"importOrder": ["^[./]", ".*", ".scss$"],
"importOrderSeparation": true,
"importOrderSortSpecifiers": true
}

View file

@ -1,161 +1,144 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta <meta
name="viewport" name="viewport"
content="width=device-width, initial-scale=1.0, viewport-fit=cover" content="width=device-width, initial-scale=1.0, viewport-fit=cover"
/> />
<meta <meta
name="description" name="description"
content="Easily merge three versions of a text document with this 3-way text merge tool." content="Easily merge three versions of a text document with this 3-way text merge tool."
/> />
<meta property="og:title" content="3-Way Text Merge" /> <meta property="og:title" content="3-Way Text Merge" />
<meta <meta
property="og:description" property="og:description"
content="Easily merge three versions of a text document with this 3-way text merge tool." content="Easily merge three versions of a text document with this 3-way text merge tool."
/> />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta <meta property="og:url" content="https://github.com/schmelczer/reconcile" />
property="og:url" <meta property="og:image" content="/favicon.ico" />
content="https://github.com/schmelczer/reconcile" <link rel="icon" type="image/x-icon" href="favicon.ico" />
/> <title>3-Way Text Merge</title>
<meta property="og:image" content="/favicon.ico" /> <link inline inline-asset="index.css" inline-asset-delete />
<link rel="icon" type="image/x-icon" href="favicon.ico" /> </head>
<title>3-Way Text Merge</title> <body>
<link rel="stylesheet" href="style.css" /> <div class="background"></div>
</head>
<body>
<div class="background"></div>
<div class="page-wrapper"> <div class="page-wrapper">
<header> <header>
<h1>3-Way Text Merge</h1> <h1>3-Way Text Merge</h1>
<p> <p>
The The
<a <a href="https://github.com/schmelczer/reconcile" target="_blank">reconcile</a>
href="https://github.com/schmelczer/reconcile" solves a fundamental challenge in collaborative editing: what happens when
target="_blank" multiple people edit the same text simultaneously?
>reconcile</a <code>reconcile(parent: str, left: str, right: str) -> str</code>
> takes conflicting concurrent edits and intelligently merges them into a unified
solves a fundamental challenge in collaborative editing: result. Beyond basic conflict resolution, it offers sophisticated merging
what happens when multiple people edit the same text heuristics, flexible tokenization options, and cursor position tracking.
simultaneously? </p>
<code <p>
>reconcile(parent: str, left: str, right: str) -> The algorithm begins with your chosen tokenizer, then applies Myers' diff
str</code algorithm to compare the original text with both conflicting versions. These
> diffs undergo transformation to preserve meaningful change sequences, before a
takes conflicting concurrent edits and intelligently merges final merge strategy—inspired by Operational Transformation (OT)—reconciles all
them into a unified result. Beyond basic conflict conflicting modifications without losing any edits.
resolution, it offers sophisticated merging heuristics, </p>
flexible tokenization options, and cursor position tracking. <p>
</p> For more details, see the
<p> <a href="https://github.com/schmelczer/reconcile" target="_blank">README</a>.
The algorithm begins with your chosen tokenizer, then </p>
applies Myers' diff algorithm to compare the original text </header>
with both conflicting versions. These diffs undergo
transformation to preserve meaningful change sequences,
before a final merge strategy—inspired by Operational
Transformation (OT)—reconciles all conflicting modifications
without losing any edits.
</p>
<p>
For more details, see the
<a
href="https://github.com/schmelczer/reconcile"
target="_blank"
>README</a
>.
</p>
</header>
<main> <main>
<div class="text-area-card diamond-parent"> <div class="text-area-card diamond-parent">
<label <label
for="original" for="original"
title="The text document's content before any concurrent edits occurred." title="The text document's content before any concurrent edits occurred."
>Original</label >Original</label
> >
<textarea id="original" name="original"></textarea> <textarea id="original" name="original"></textarea>
</div>
<div class="text-area-card diamond-left">
<label
for="left"
title="Colour-coded tokens mark the origin of each token in the result. This text box is marked with the colour green."
>
First concurrent edit
<div class="box Left"></div>
</label>
<textarea id="left" name="left"></textarea>
</div>
<div class="text-area-card diamond-right">
<label
for="right"
title="Colour-coded tokens mark the origin of each token in the result. This text box is marked with the colour blue."
>
Second concurrent edit
<div class="box Right"></div>
</label>
<textarea id="right" name="right"></textarea>
</div>
<div class="text-area-card diamond-result">
<label
title="Read-only. Change the above text boxes to change the content of this box."
>
Deconflicted result
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M10 10l-6 6v4h4l6 -6m1.99 -1.99l2.504 -2.504a2.828 2.828 0 1 0 -4 -4l-2.5 2.5"
/>
<path d="M13.5 6.5l4 4" />
<path d="M3 3l18 18" />
</svg>
</label>
<div id="merged"></div>
</div>
</main>
<footer>
<p>2025 Andras Schmelczer</p>
<a
href="https://github.com/schmelczer/reconcile"
class="github-link"
aria-label="GitHub repository"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M9 19c-4.3 1.4 -4.3 -2.5 -6 -3m12 5v-3.5c0 -1 .1 -1.4 -.5 -2c2.8 -.3 5.5 -1.4 5.5 -6a4.6 4.6 0 0 0 -1.3 -3.2a4.2 4.2 0 0 0 -.1 -3.2s-1.1 -.3 -3.5 1.3a12.3 12.3 0 0 0 -6.2 0c-2.4 -1.6 -3.5 -1.3 -3.5 -1.3a4.2 4.2 0 0 0 -.1 3.2a4.6 4.6 0 0 0 -1.3 3.2c0 4.6 2.7 5.7 5.5 6c-.6 .6 -.6 1.2 -.5 2v3.5"
/>
</svg>
</a>
</footer>
</div> </div>
<script type="module" src="script.js"></script> <div class="text-area-card diamond-left">
</body> <label
for="left"
title="Colour-coded tokens mark the origin of each token in the result. This text box is marked with the colour green."
>
First concurrent edit
<div class="box Left"></div>
</label>
<textarea id="left" name="left"></textarea>
</div>
<div class="text-area-card diamond-right">
<label
for="right"
title="Colour-coded tokens mark the origin of each token in the result. This text box is marked with the colour blue."
>
Second concurrent edit
<div class="box Right"></div>
</label>
<textarea id="right" name="right"></textarea>
</div>
<div class="text-area-card diamond-result">
<label
title="Read-only. Change the above text boxes to change the content of this box."
>
Deconflicted result
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M10 10l-6 6v4h4l6 -6m1.99 -1.99l2.504 -2.504a2.828 2.828 0 1 0 -4 -4l-2.5 2.5"
/>
<path d="M13.5 6.5l4 4" />
<path d="M3 3l18 18" />
</svg>
</label>
<div id="merged"></div>
</div>
</main>
<footer>
<p>2025 Andras Schmelczer</p>
<a
href="https://github.com/schmelczer/reconcile"
class="github-link"
aria-label="GitHub repository"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M9 19c-4.3 1.4 -4.3 -2.5 -6 -3m12 5v-3.5c0 -1 .1 -1.4 -.5 -2c2.8 -.3 5.5 -1.4 5.5 -6a4.6 4.6 0 0 0 -1.3 -3.2a4.2 4.2 0 0 0 -.1 -3.2s-1.1 -.3 -3.5 1.3a12.3 12.3 0 0 0 -6.2 0c-2.4 -1.6 -3.5 -1.3 -3.5 -1.3a4.2 4.2 0 0 0 -.1 3.2a4.6 4.6 0 0 0 -1.3 3.2c0 4.6 2.7 5.7 5.5 6c-.6 .6 -.6 1.2 -.5 2v3.5"
/>
</svg>
</a>
</footer>
</div>
<noscript>JavaScript is required for this website.</noscript>
<script inline inline-asset="index.js" inline-asset-delete></script>
</body>
</html> </html>

80
examples/website/index.ts Normal file
View file

@ -0,0 +1,80 @@
import { init, reconcileWithHistory } from 'reconcile';
import './style.scss';
const originalTextArea = document.getElementById('original') as HTMLTextAreaElement;
const leftTextArea = document.getElementById('left') as HTMLTextAreaElement;
const rightTextArea = document.getElementById('right') as HTMLTextAreaElement;
const mergedTextArea = document.getElementById('merged') as HTMLDivElement;
const sampleText = `The \`reconcile\` Rust library is embedded on this page a WASM module and it powers these text boxes. Experiment with the "Original", "First concurrent edit", and "Second concurrent edit" text boxes to watch competing changes merge in real-time within the "Deconflicted result" box. Here, you will see color-coded tokens marking the origin of each token, including ones that got deleted. The result highly depends on the tokenization strategy, for example, deciding how casing or white-spacing is taken into account.`;
async function main(): Promise<void> {
await init();
originalTextArea?.addEventListener('input', updateMergedText);
leftTextArea?.addEventListener('input', updateMergedText);
rightTextArea?.addEventListener('input', updateMergedText);
window.addEventListener('resize', resizeTextAreas);
loadSample();
updateMergedText();
if (leftTextArea) focusTextArea(leftTextArea);
}
function loadSample(): void {
if (originalTextArea) originalTextArea.value = sampleText;
if (leftTextArea) {
leftTextArea.value =
sampleText.replace('color', 'colour') +
" Check out what's the most complex conflict you can come up with!";
}
if (rightTextArea) {
rightTextArea.value = sampleText
.replace(', for example,', ' such as')
.replace('WASM', 'WebAssembly');
}
}
function updateMergedText(): void {
resizeTextAreas();
if (!originalTextArea || !leftTextArea || !rightTextArea || !mergedTextArea) {
return;
}
const original = originalTextArea.value;
const left = leftTextArea.value;
const right = rightTextArea.value;
const results = reconcileWithHistory(original, left, right);
mergedTextArea.innerHTML = '';
for (const { text, history } of results.history) {
const span = document.createElement('span');
span.className = history;
span.textContent = text;
mergedTextArea.appendChild(span);
}
}
function resizeTextAreas(): void {
if (!CSS.supports('field-sizing', 'content')) {
if (originalTextArea) autoResize(originalTextArea);
if (leftTextArea) autoResize(leftTextArea);
if (rightTextArea) autoResize(rightTextArea);
}
}
function autoResize(textarea: HTMLTextAreaElement): void {
textarea.style.height = 'auto';
textarea.style.height = textarea.scrollHeight + 'px';
}
function focusTextArea(textarea: HTMLTextAreaElement): void {
textarea.focus();
textarea.selectionStart = textarea.value.length;
textarea.selectionEnd = textarea.value.length;
}
main();

5555
examples/website/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,48 @@
{
"name": "portfolio",
"description": "An easily configurable timeline of projects.",
"private": true,
"scripts": {
"start": "webpack serve --open --mode development",
"format": "prettier --write \"src/**/*.(ts|scss|json|html)\"",
"build": "webpack --mode production"
},
"repository": {
"type": "git",
"url": "git+https://github.com/schmelczer/schmelczer.github.io.git"
},
"keywords": [
"CV",
"curriculum",
"vitae",
"portfolio",
"resumé"
],
"author": "Andras Schmelczer",
"license": "GPL-3.0-or-later",
"bugs": {
"url": "https://github.com/schmelczer/schmelczer.github.io/issues"
},
"browserslist": [
"defaults"
],
"homepage": "https://github.com/schmelczer/schmelczer.github.io#readme",
"devDependencies": {
"reconcile": "file:../../reconcile-js",
"css-loader": "^7.1.2",
"html-webpack-plugin": "^5.6.3",
"inline-source-webpack-plugin": "^3.0.1",
"mini-css-extract-plugin": "^2.9.2",
"prettier": "^3.6.2",
"resolve-url-loader": "^5.0.0",
"sass": "^1.89.2",
"sass-loader": "^16.0.5",
"svg-inline-loader": "^0.8.2",
"terser-webpack-plugin": "^5.3.14",
"ts-loader": "^9.5.2",
"typescript": "^5.8.3",
"webpack": "^5.99.9",
"webpack-cli": "^6.0.1",
"webpack-dev-server": "^5.2.2"
}
}

View file

@ -1,71 +0,0 @@
import { init, reconcileWithHistory } from "./dist/index.js";
const originalTextArea = document.getElementById("original");
const leftTextArea = document.getElementById("left");
const rightTextArea = document.getElementById("right");
const mergedTextArea = document.getElementById("merged");
const sampleText = `The \`reconcile\` Rust library is embedded on this page a WASM module and it powers these text boxes. Experiment with the "Original", "First concurrent edit", and "Second concurrent edit" text boxes to watch competing changes merge in real-time within the "Deconflicted result" box. Here, you will see color-coded tokens marking the origin of each token, including ones that got deleted. The result highly depends on the tokenization strategy, for example, deciding how casing or white-spacing is taken into account.`;
async function main() {
await init();
originalTextArea.addEventListener("input", updateMergedText);
leftTextArea.addEventListener("input", updateMergedText);
rightTextArea.addEventListener("input", updateMergedText);
window.addEventListener("resize", resizeTextAreas);
loadSample();
updateMergedText();
focusTextArea(leftTextArea);
}
function loadSample() {
originalTextArea.value = sampleText;
leftTextArea.value =
sampleText.replace("color", "colour") +
" Check out what's the most complex conflict you can come up with!";
rightTextArea.value = sampleText
.replace(", for example,", " such as")
.replace("WASM", "WebAssembly");
}
function updateMergedText() {
resizeTextAreas();
const original = originalTextArea.value;
const left = leftTextArea.value;
const right = rightTextArea.value;
const results = reconcileWithHistory(original, left, right);
mergedTextArea.innerHTML = "";
for (const {text, history} of results.history) {
const span = document.createElement("span");
span.className = history;
span.textContent = text;
mergedTextArea.appendChild(span);
}
}
function resizeTextAreas() {
if (!CSS.supports("field-sizing", "content")) {
autoResize(originalTextArea);
autoResize(leftTextArea);
autoResize(rightTextArea);
}
}
function autoResize(textarea) {
textarea.style.height = "auto";
textarea.style.height = textarea.scrollHeight + "px";
}
function focusTextArea(textarea) {
textarea.focus();
textarea.selectionStart = textarea.value.length;
textarea.selectionEnd = textarea.value.length;
}
main();

View file

@ -1,241 +0,0 @@
* {
box-sizing: border-box;
margin: 0;
user-select: none;
}
html,
body {
height: 100%;
}
body {
font-family: "Segoe UI", Arial, sans-serif;
color: #23272f;
overflow-y: auto;
}
.background {
background: linear-gradient(135deg, #f8fafc 0%, #e0e7ef 100%);
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: -1;
}
.page-wrapper {
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 100%;
max-width: 1000px;
margin: 0 auto;
}
header {
padding: 32px 32px 0 32px;
}
header > h1 {
font-size: 2.5rem;
font-weight: 700;
color: #2451a6;
margin-bottom: 24px;
text-align: center;
}
p,
p * {
user-select: text;
}
header > p {
color: #5a6272;
font-size: 1.1rem;
margin-bottom: 0;
}
header > p:not(:first-of-type) {
margin-top: 16px;
}
main {
display: grid;
grid-template-rows: min-content;
grid-template-columns: 1fr 1fr;
gap: 20px;
justify-items: center;
align-items: center;
padding: 32px;
}
.diamond-parent {
grid-column: 1 / -1;
}
.diamond-left {
grid-column: 1;
grid-row: 2;
}
.diamond-right {
grid-column: 2;
grid-row: 2;
}
.diamond-result {
grid-column: 1 / -1;
grid-row: 3;
}
.diamond-result label {
display: flex;
align-items: center;
}
.diamond-result svg {
width: 20px;
height: 20px;
margin-left: 8px;
}
.text-area-card {
width: 100%;
height: 100%;
background: #fff;
border-radius: 10px;
box-shadow: 0 2px 12px 0 rgba(36, 81, 166, 0.06);
padding: 18px 20px 16px 20px;
margin-bottom: 0;
}
label {
display: inline-block;
margin-bottom: 8px;
font-weight: 600;
color: #2451a6;
cursor: help;
}
.box {
width: 1ch;
height: 1ch;
border-radius: 50%;
margin-left: 6px;
display: inline-block;
transform: scale(1.5);
}
textarea {
width: 100%;
border: none;
font-size: 1rem;
font-family: inherit;
color: #23272f;
box-sizing: border-box;
resize: none;
outline: none;
margin-bottom: 0;
field-sizing: content; /* Doesn't work in Safari yet */
}
#merged {
width: 100%;
user-select: text;
}
.Unchanged {
user-select: text;
}
.AddedFromLeft,
.RemovedFromLeft {
user-select: text;
background: #12d197;
}
.Right,
.AddedFromRight,
.RemovedFromRight {
user-select: text;
background: #85bff7;
}
.RemovedFromLeft,
.RemovedFromRight {
user-select: none;
text-decoration: line-through;
}
@media (max-width: 900px) {
header {
padding: 32px 18px 0 18px;
}
header > h1 {
margin-bottom: 18px;
}
header > p {
font-size: 1rem;
}
main {
padding: 18px;
}
}
@media (max-width: 768px) {
main {
grid-template-columns: 1fr;
grid-template-rows: auto auto auto auto;
}
.diamond-parent {
grid-column: 1;
grid-row: 1;
}
.diamond-left {
grid-column: 1;
grid-row: 2;
}
.diamond-right {
grid-column: 1;
grid-row: 3;
}
.diamond-result {
grid-column: 1;
grid-row: 4;
}
}
footer {
padding: 16px;
width: 100%;
position: relative;
display: flex;
justify-content: center;
align-items: center;
color: #5a6272;
}
.github-link > svg {
position: absolute;
color: #5a6272;
top: 50%;
right: 36px;
transform: translateY(-50%);
width: 32px;
height: 32px;
transition: transform 0.2s;
}
.github-link > svg:hover {
cursor: pointer;
transform: translateY(-50%) scale(1.15);
}

242
examples/website/style.scss Normal file
View file

@ -0,0 +1,242 @@
* {
box-sizing: border-box;
margin: 0;
user-select: none;
}
html,
body {
height: 100%;
}
body {
font-family: 'Segoe UI', Arial, sans-serif;
color: #23272f;
overflow-y: auto;
}
.background {
background: linear-gradient(135deg, #f8fafc 0%, #e0e7ef 100%);
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: -1;
}
.page-wrapper {
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 100%;
max-width: 1000px;
margin: 0 auto;
}
header {
padding: 32px 32px 0 32px;
}
header > h1 {
font-size: 2.5rem;
font-weight: 700;
color: #2451a6;
margin-bottom: 24px;
text-align: center;
}
p,
p * {
user-select: text;
}
header > p {
color: #5a6272;
font-size: 1.1rem;
margin-bottom: 0;
}
header > p:not(:first-of-type) {
margin-top: 16px;
}
main {
display: grid;
grid-template-rows: min-content;
grid-template-columns: 1fr 1fr;
gap: 20px;
justify-items: center;
align-items: center;
padding: 32px;
}
.diamond-parent {
grid-column: 1 / -1;
}
.diamond-left {
grid-column: 1;
grid-row: 2;
}
.diamond-right {
grid-column: 2;
grid-row: 2;
}
.diamond-result {
grid-column: 1 / -1;
grid-row: 3;
}
.diamond-result label {
display: flex;
align-items: center;
}
.diamond-result svg {
width: 20px;
height: 20px;
margin-left: 8px;
}
.text-area-card {
width: 100%;
height: 100%;
background: #fff;
border-radius: 10px;
box-shadow: 0 2px 12px 0 rgba(36, 81, 166, 0.06);
padding: 18px 20px 16px 20px;
margin-bottom: 0;
}
label {
display: inline-block;
margin-bottom: 8px;
font-weight: 600;
color: #2451a6;
cursor: help;
}
.box {
width: 1ch;
height: 1ch;
border-radius: 50%;
margin-left: 6px;
display: inline-block;
transform: scale(1.5);
}
textarea {
width: 100%;
border: none;
font-size: 1rem;
font-family: inherit;
color: #23272f;
box-sizing: border-box;
resize: none;
outline: none;
margin-bottom: 0;
field-sizing: content; /* Doesn't work in Safari yet */
}
#merged {
width: 100%;
user-select: text;
}
.Unchanged {
user-select: text;
}
.Left,
.AddedFromLeft,
.RemovedFromLeft {
user-select: text;
background: #12d197;
}
.Right,
.AddedFromRight,
.RemovedFromRight {
user-select: text;
background: #85bff7;
}
.RemovedFromLeft,
.RemovedFromRight {
user-select: none;
text-decoration: line-through;
}
@media (max-width: 900px) {
header {
padding: 32px 18px 0 18px;
}
header > h1 {
margin-bottom: 18px;
}
header > p {
font-size: 1rem;
}
main {
padding: 18px;
}
}
@media (max-width: 768px) {
main {
grid-template-columns: 1fr;
grid-template-rows: auto auto auto auto;
}
.diamond-parent {
grid-column: 1;
grid-row: 1;
}
.diamond-left {
grid-column: 1;
grid-row: 2;
}
.diamond-right {
grid-column: 1;
grid-row: 3;
}
.diamond-result {
grid-column: 1;
grid-row: 4;
}
}
footer {
padding: 16px;
width: 100%;
position: relative;
display: flex;
justify-content: center;
align-items: center;
color: #5a6272;
}
.github-link > svg {
position: absolute;
color: #5a6272;
top: 50%;
right: 36px;
transform: translateY(-50%);
width: 32px;
height: 32px;
transition: transform 0.2s;
}
.github-link > svg:hover {
cursor: pointer;
transform: translateY(-50%) scale(1.15);
}

View file

@ -0,0 +1,19 @@
{
"compilerOptions": {
"module": "ESNext",
"target": "ESNext",
"strict": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"moduleResolution": "bundler",
"declaration": true,
"declarationDir": "./dist/types",
"outDir": "./dist",
"rootDir": ".",
"skipLibCheck": true,
"inlineSourceMap": true
},
"exclude": [
"./dist"
]
}

View file

@ -0,0 +1,81 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const InlineSourceWebpackPlugin = require('inline-source-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = (_env, argv) => ({
devtool: argv.mode === 'development' ? 'inline-source-map' : false,
entry: {
index: './index.ts',
},
devServer: {
allowedHosts: 'all',
},
watchOptions: {
ignored: '**/node_modules',
},
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
module: true,
},
}),
],
},
performance: {
assetFilter: (f) => !/\.(webm|mp4|pdf)$/.test(f),
maxEntrypointSize: 100000,
maxAssetSize: 512000,
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html',
}),
new MiniCssExtractPlugin(),
argv.mode === 'production'
? new InlineSourceWebpackPlugin({
compress: true,
})
: null,
].filter(Boolean),
module: {
rules: [
{
test: /\.svg$/i,
use: 'svg-inline-loader',
},
{
test: /\.scss$/i,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'resolve-url-loader',
{
loader: 'sass-loader',
options: {
sourceMap: true, // required by resolve-url-loader
},
},
],
},
{
test: /\.ts$/,
use: 'ts-loader',
},
],
},
resolve: {
extensions: [
'.ts',
'.js', // required for development
],
},
output: {
clean: true,
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
publicPath: '',
},
});

6
package-lock.json generated Normal file
View file

@ -0,0 +1,6 @@
{
"name": "reconcile",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}

View file

@ -5,9 +5,5 @@ set -e
wasm-pack build --target web --features wasm,wee_alloc wasm-pack build --target web --features wasm,wee_alloc
cd reconcile-js cd reconcile-js
npm run build npm run build
mkdir -p ../examples/website/dist
cp -R dist/index.js ../examples/website/dist/index.js
cd ../examples/website cd ../examples/website
npm run start
python3 -m http.server $1 --bind 0.0.0.0