diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 9e6f1e48c..beb20c8be 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -28,7 +28,7 @@ updates:
prefix: npm dependencies (development)
include: scope
- package-ecosystem: npm
- directory: betty/extension/cotton_candy/assets/betty.extension.npm._Npm/src/
+ directory: betty/extension/cotton_candy/webpack/
schedule:
interval: weekly
assignees:
@@ -37,7 +37,7 @@ updates:
prefix: npm dependencies (Cotton Candy extension)
include: scope
- package-ecosystem: npm
- directory: betty/extension/http_api_doc/assets/betty.extension.npm._Npm/src/
+ directory: betty/extension/http_api_doc/webpack/
schedule:
interval: weekly
assignees:
@@ -46,7 +46,7 @@ updates:
prefix: npm dependencies (HttpApiDoc extension)
include: scope
- package-ecosystem: npm
- directory: betty/extension/maps/assets/betty.extension.npm._Npm/src/
+ directory: betty/extension/maps/webpack/
schedule:
interval: weekly
assignees:
@@ -55,7 +55,7 @@ updates:
prefix: npm dependencies (Maps extension)
include: scope
- package-ecosystem: npm
- directory: betty/extension/trees/assets/betty.extension.npm._Npm/src/
+ directory: betty/extension/trees/webpack/
schedule:
interval: weekly
assignees:
diff --git a/.gitignore b/.gitignore
index 5a5d8e430..6974e7772 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@ __pycache__
/betty.egg-info
/build
/dist
+/prebuild
*.pyc
*.pyc.*
diff --git a/.stylelintignore b/.stylelintignore
index 6bde87fef..f01501f4d 100644
--- a/.stylelintignore
+++ b/.stylelintignore
@@ -1 +1 @@
-betty/**/assets/betty.extension.npm._Npm/build/*
+prebuild/**/*
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 000000000..670c1e756
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1 @@
+prune node_modules
diff --git a/betty/_npm.py b/betty/_npm.py
new file mode 100644
index 000000000..06f5854a4
--- /dev/null
+++ b/betty/_npm.py
@@ -0,0 +1,81 @@
+"""
+Provide tools to integrate extensions with `npm `_.
+
+This module is internal.
+"""
+
+from __future__ import annotations
+
+import logging
+import sys
+from asyncio import subprocess as aiosubprocess
+from pathlib import Path
+from typing import Sequence
+
+from betty.asyncio import wait_to_thread
+from betty.error import UserFacingError
+from betty.locale import Str, DEFAULT_LOCALIZER
+from betty.requirement import Requirement
+from betty.subprocess import run_process
+
+
+_NPM_UNAVAILABLE_MESSAGE = Str._(
+ "npm (https://www.npmjs.com/) must be available for features that require Node.js packages to be installed. Ensure that the `npm` executable is available in your `PATH`."
+)
+
+
+class NpmUnavailable(UserFacingError, RuntimeError):
+ def __init__(self):
+ super().__init__(_NPM_UNAVAILABLE_MESSAGE)
+
+
+async def npm(
+ arguments: Sequence[str],
+ cwd: Path | None = None,
+) -> aiosubprocess.Process:
+ """
+ Run an npm command.
+ """
+ try:
+ return await run_process(
+ ["npm", *arguments],
+ cwd=cwd,
+ # Use a shell on Windows so subprocess can find the executables it needs (see
+ # https://bugs.python.org/issue17023).
+ shell=sys.platform.startswith("win32"),
+ )
+ except FileNotFoundError:
+ raise NpmUnavailable()
+
+
+class NpmRequirement(Requirement):
+ def __init__(self):
+ super().__init__()
+ self._met: bool
+ self._summary: Str
+ self._details = _NPM_UNAVAILABLE_MESSAGE
+
+ def _check(self) -> None:
+ if hasattr(self, "_met"):
+ return
+ try:
+ wait_to_thread(npm(["--version"]))
+ except NpmUnavailable:
+ self._met = False
+ self._summary = Str._("`npm` is not available")
+ else:
+ self._met = True
+ self._summary = Str._("`npm` is available")
+ finally:
+ logging.getLogger(__name__).debug(self._summary.localize(DEFAULT_LOCALIZER))
+
+ def is_met(self) -> bool:
+ self._check()
+ return self._met
+
+ def summary(self) -> Str:
+ self._check()
+ return self._summary
+
+ def details(self) -> Str:
+ return self._details
diff --git a/betty/_package/pyinstaller/__init__.py b/betty/_package/pyinstaller/__init__.py
index 8f5d59e3f..6cdc6b2d7 100644
--- a/betty/_package/pyinstaller/__init__.py
+++ b/betty/_package/pyinstaller/__init__.py
@@ -10,33 +10,31 @@
from betty._package.pyinstaller.hooks import HOOKS_DIRECTORY_PATH
from betty.app import App
-from betty.app.extension import discover_extension_types, Extension
-from betty.asyncio import gather
-from betty.extension.npm import _Npm, build_assets, _NpmBuilder
+from betty.app.extension import discover_extension_types
+from betty.extension.webpack import (
+ Webpack,
+ WebpackEntrypointProvider,
+)
from betty.fs import ROOT_DIRECTORY_PATH
-from betty.project import ExtensionConfiguration
+from betty.job import Context
-async def _build_assets() -> None:
- npm_builder_extension_types: list[type[_NpmBuilder & Extension]] = [
- extension_type
- for extension_type in discover_extension_types()
- if issubclass(extension_type, _NpmBuilder)
- ]
+async def prebuild_webpack_assets() -> None:
+ """
+ Prebuild Webpack assets for inclusion in package builds.
+ """
+ job_context = Context()
async with App.new_temporary() as app, app:
- app.project.configuration.extensions.append(ExtensionConfiguration(_Npm))
- for extension_type in npm_builder_extension_types:
- app.project.configuration.extensions.append(
- ExtensionConfiguration(extension_type)
- )
- await gather(
- *(
- [
- build_assets(app.extensions[extension_type]) # type: ignore[arg-type]
- for extension_type in npm_builder_extension_types
- ]
- )
+ app.project.configuration.extensions.enable(Webpack)
+ webpack = app.extensions[Webpack]
+ app.project.configuration.extensions.enable(
+ *{
+ extension_type
+ for extension_type in discover_extension_types()
+ if issubclass(extension_type, WebpackEntrypointProvider)
+ }
)
+ await webpack.prebuild(job_context=job_context)
async def a_pyz_exe_coll() -> tuple[Analysis, PYZ, EXE, COLLECT]:
@@ -52,12 +50,18 @@ async def a_pyz_exe_coll() -> tuple[Analysis, PYZ, EXE, COLLECT]:
else:
raise RuntimeError(f"Unsupported platform {sys.platform}.")
- await _build_assets()
+ await prebuild_webpack_assets()
block_cipher = None
datas = []
data_file_path_patterns = [
+ # Assets.
"betty/assets/**",
"betty/extension/*/assets/**",
+ # Webpack.
+ ".browserslistrc",
+ "betty/extension/*/webpack/**",
+ "tsconfig.json",
+ "prebuild/**",
]
for data_file_path_pattern in data_file_path_patterns:
for data_file_path_str in glob(
diff --git a/betty/assets/betty.pot b/betty/assets/betty.pot
index dd3767909..b8a52132f 100644
--- a/betty/assets/betty.pot
+++ b/betty/assets/betty.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Betty VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2024-03-27 23:37+0000\n"
+"POT-Creation-Date: 2024-04-30 22:56+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -189,16 +189,7 @@ msgstr ""
msgid "Birth"
msgstr ""
-msgid "Built the Cotton Candy front-end assets."
-msgstr ""
-
-msgid "Built the HTTP API documentation."
-msgstr ""
-
-msgid "Built the interactive family trees."
-msgstr ""
-
-msgid "Built the interactive maps."
+msgid "Built the Webpack front-end assets."
msgstr ""
msgid "Burial"
@@ -590,10 +581,10 @@ msgstr ""
msgid "Places"
msgstr ""
-msgid "Pre-built assets"
+msgid "Pre-built Webpack front-end assets are available"
msgstr ""
-msgid "Pre-built assets are unavailable for {extension_names}."
+msgid "Pre-built Webpack front-end assets are unavailable"
msgstr ""
msgid "Presence"
diff --git a/betty/assets/locale/de-DE/betty.po b/betty/assets/locale/de-DE/betty.po
index ac42bbac5..5a34f2701 100644
--- a/betty/assets/locale/de-DE/betty.po
+++ b/betty/assets/locale/de-DE/betty.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Betty VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2024-03-27 23:37+0000\n"
+"POT-Creation-Date: 2024-04-30 22:56+0100\n"
"PO-Revision-Date: 2024-02-08 13:24+0000\n"
"Last-Translator: Bart Feenstra \n"
"Language: de\n"
@@ -279,17 +279,8 @@ msgstr "Betty-Projektkonfiguration ({supported_formats})"
msgid "Birth"
msgstr "Geburt"
-msgid "Built the Cotton Candy front-end assets."
-msgstr "Cotton Candy front-end assets erstellt."
-
-msgid "Built the HTTP API documentation."
-msgstr "Die HTTP-API-Dokumentation wurde erstellt."
-
-msgid "Built the interactive family trees."
-msgstr "Die interaktiven Stammbäume sind erstellt."
-
-msgid "Built the interactive maps."
-msgstr "Die interaktiven Karten wurden erstellt."
+msgid "Built the Webpack front-end assets."
+msgstr "Webpack front-end assets erstellt."
msgid "Burial"
msgstr "Beerdigung"
@@ -760,11 +751,11 @@ msgstr "Ort"
msgid "Places"
msgstr "Orte"
-msgid "Pre-built assets"
-msgstr "Vorgefertigte Objekte"
+msgid "Pre-built Webpack front-end assets are available"
+msgstr "Vorgefertigte Webpack front-end assets sind verfügbar"
-msgid "Pre-built assets are unavailable for {extension_names}."
-msgstr "Vorgefertigte Objekte für {extension_names} sind nicht verfügbar."
+msgid "Pre-built Webpack front-end assets are unavailable"
+msgstr "Vorgefertigte Webpack front-end assets sind nicht verfügbar"
msgid "Presence"
msgstr "Anwesenheit"
diff --git a/betty/assets/locale/fr-FR/betty.po b/betty/assets/locale/fr-FR/betty.po
index d42baadbe..c12b69373 100644
--- a/betty/assets/locale/fr-FR/betty.po
+++ b/betty/assets/locale/fr-FR/betty.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2024-03-27 23:37+0000\n"
+"POT-Creation-Date: 2024-04-30 22:56+0100\n"
"PO-Revision-Date: 2024-02-08 13:24+0000\n"
"Last-Translator: Bart Feenstra \n"
"Language: fr\n"
@@ -233,16 +233,7 @@ msgstr ""
msgid "Birth"
msgstr "Naissance"
-msgid "Built the Cotton Candy front-end assets."
-msgstr ""
-
-msgid "Built the HTTP API documentation."
-msgstr ""
-
-msgid "Built the interactive family trees."
-msgstr ""
-
-msgid "Built the interactive maps."
+msgid "Built the Webpack front-end assets."
msgstr ""
msgid "Burial"
@@ -674,10 +665,10 @@ msgstr "Lieu"
msgid "Places"
msgstr "Lieux"
-msgid "Pre-built assets"
+msgid "Pre-built Webpack front-end assets are available"
msgstr ""
-msgid "Pre-built assets are unavailable for {extension_names}."
+msgid "Pre-built Webpack front-end assets are unavailable"
msgstr ""
msgid "Presence"
diff --git a/betty/assets/locale/nl-NL/betty.po b/betty/assets/locale/nl-NL/betty.po
index 7664c9c96..e20068122 100644
--- a/betty/assets/locale/nl-NL/betty.po
+++ b/betty/assets/locale/nl-NL/betty.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2024-03-27 23:37+0000\n"
+"POT-Creation-Date: 2024-04-30 22:56+0100\n"
"PO-Revision-Date: 2024-02-11 15:31+0000\n"
"Last-Translator: Bart Feenstra \n"
"Language: nl\n"
@@ -276,17 +276,8 @@ msgstr "Betty-projectconfiguratie ({supported_formats})"
msgid "Birth"
msgstr "Geboorte"
-msgid "Built the Cotton Candy front-end assets."
-msgstr "De Cotton Candy front-endassets gegenereerd."
-
-msgid "Built the HTTP API documentation."
-msgstr "HTTP API-documentatie gegenereerd."
-
-msgid "Built the interactive family trees."
-msgstr "De interactieve stambomen gegenereerd."
-
-msgid "Built the interactive maps."
-msgstr "De interactieve kaarten gegenereerd."
+msgid "Built the Webpack front-end assets."
+msgstr "De Webpack front-end assets gebouwd."
msgid "Burial"
msgstr "Begravenis"
@@ -755,11 +746,11 @@ msgstr "Plaats"
msgid "Places"
msgstr "Plaatsen"
-msgid "Pre-built assets"
-msgstr "Kant-en-klare assets"
+msgid "Pre-built Webpack front-end assets are available"
+msgstr "Vooraf gebouwde Webpack front-end assets zijn beschikbaar."
-msgid "Pre-built assets are unavailable for {extension_names}."
-msgstr "Kant-en-klare assets zijn beschikbaar voor {extension_names}."
+msgid "Pre-built Webpack front-end assets are unavailable"
+msgstr "Vooraf gebouwde Webpack front-end assets zijn niet beschikbaar."
msgid "Presence"
msgstr "Aanwezigheid"
diff --git a/betty/assets/locale/uk/betty.po b/betty/assets/locale/uk/betty.po
index b926d05df..6cce4c192 100644
--- a/betty/assets/locale/uk/betty.po
+++ b/betty/assets/locale/uk/betty.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Betty VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2024-03-27 23:37+0000\n"
+"POT-Creation-Date: 2024-04-30 22:56+0100\n"
"PO-Revision-Date: 2024-02-08 13:08+0000\n"
"Last-Translator: Rainer Thieringer \n"
"Language: uk\n"
@@ -233,16 +233,7 @@ msgstr ""
msgid "Birth"
msgstr "Народження"
-msgid "Built the Cotton Candy front-end assets."
-msgstr ""
-
-msgid "Built the HTTP API documentation."
-msgstr ""
-
-msgid "Built the interactive family trees."
-msgstr ""
-
-msgid "Built the interactive maps."
+msgid "Built the Webpack front-end assets."
msgstr ""
msgid "Burial"
@@ -674,10 +665,10 @@ msgstr "Місце"
msgid "Places"
msgstr "Місця"
-msgid "Pre-built assets"
+msgid "Pre-built Webpack front-end assets are available"
msgstr ""
-msgid "Pre-built assets are unavailable for {extension_names}."
+msgid "Pre-built Webpack front-end assets are unavailable"
msgstr ""
msgid "Presence"
diff --git a/betty/assets/templates/head.html.j2 b/betty/assets/templates/head.html.j2
index da0dadd47..f95989174 100644
--- a/betty/assets/templates/head.html.j2
+++ b/betty/assets/templates/head.html.j2
@@ -41,9 +41,3 @@
{% endif %}
{% endif %}
-{% for css_path in public_css_paths %}
-
-{% endfor %}
-{% for js_path in public_js_paths %}
-
-{% endfor %}
\ No newline at end of file
diff --git a/betty/assets/templates/scripts.html.j2 b/betty/assets/templates/scripts.html.j2
new file mode 100644
index 000000000..ccb385c9d
--- /dev/null
+++ b/betty/assets/templates/scripts.html.j2
@@ -0,0 +1,3 @@
+{% for js_path in public_js_paths | unique %}
+
+{% endfor %}
diff --git a/betty/assets/templates/stylesheets.html.j2 b/betty/assets/templates/stylesheets.html.j2
new file mode 100644
index 000000000..2bc504aad
--- /dev/null
+++ b/betty/assets/templates/stylesheets.html.j2
@@ -0,0 +1,3 @@
+{% for css_path in public_css_paths | unique %}
+
+{% endfor %}
diff --git a/betty/extension/__init__.py b/betty/extension/__init__.py
index 3da998af8..fad171b1c 100644
--- a/betty/extension/__init__.py
+++ b/betty/extension/__init__.py
@@ -11,6 +11,7 @@
from betty.extension.nginx import Nginx
from betty.extension.privatizer import Privatizer
from betty.extension.trees import Trees
+from betty.extension.webpack import Webpack
from betty.extension.wikipedia import Wikipedia
__all__ = (
@@ -23,5 +24,6 @@
"Nginx",
"Privatizer",
"Trees",
+ "Webpack",
"Wikipedia",
)
diff --git a/betty/extension/cotton_candy/__init__.py b/betty/extension/cotton_candy/__init__.py
index a583cba8b..536f41b90 100644
--- a/betty/extension/cotton_candy/__init__.py
+++ b/betty/extension/cotton_candy/__init__.py
@@ -4,27 +4,23 @@
from __future__ import annotations
-import asyncio
-import logging
import re
from collections import defaultdict
from collections.abc import Sequence, AsyncIterable
from pathlib import Path
-from shutil import copy2
from typing import Any, Callable, Iterable, Self, cast
from PyQt6.QtWidgets import QWidget
-from aiofiles.os import makedirs
from jinja2 import pass_context
from jinja2.runtime import Context
from betty.app.extension import ConfigurableExtension, Extension, Theme
from betty.config import Configuration
from betty.extension.cotton_candy.search import Index
-from betty.extension.npm import _Npm, _NpmBuilder, npm
+from betty.extension.webpack import Webpack, WebpackEntrypointProvider
from betty.functools import walk
-from betty.generate import Generator, GenerationContext
from betty.gui import GuiBuilder
+from betty.html import CssProvider
from betty.jinja2 import (
Jinja2Provider,
context_app,
@@ -221,11 +217,11 @@ def dump(self) -> VoidableDump:
class CottonCandy(
Theme,
+ CssProvider,
ConfigurableExtension[CottonCandyConfiguration],
- Generator,
GuiBuilder,
- _NpmBuilder,
Jinja2Provider,
+ WebpackEntrypointProvider,
):
@classmethod
def name(cls) -> str:
@@ -233,12 +229,39 @@ def name(cls) -> str:
@classmethod
def depends_on(cls) -> set[type[Extension]]:
- return {_Npm}
+ return {Webpack}
@classmethod
- def assets_directory_path(cls) -> Path | None:
+ def comes_after(cls) -> set[type[Extension]]:
+ from betty.extension import Maps, Trees
+
+ return {Maps, Trees}
+
+ @classmethod
+ def assets_directory_path(cls) -> Path:
return Path(__file__).parent / "assets"
+ @classmethod
+ def webpack_entrypoint_directory_path(cls) -> Path:
+ return Path(__file__).parent / "webpack"
+
+ def webpack_entrypoint_cache_keys(self) -> Sequence[str]:
+ return (
+ self._app.project.configuration.root_path,
+ self._configuration.primary_inactive_color.hex,
+ self._configuration.primary_active_color.hex,
+ self._configuration.link_inactive_color.hex,
+ self._configuration.link_active_color.hex,
+ )
+
+ @property
+ def public_css_paths(self) -> list[str]:
+ return [
+ self.app.static_url_generator.generate(
+ "css/betty.extension.CottonCandy.css"
+ ),
+ ]
+
@classmethod
def label(cls) -> Str:
return Str.plain("Cotton Candy")
@@ -271,40 +294,6 @@ def filters(self) -> dict[str, Callable[..., Any]]:
"person_descendant_families": person_descendant_families,
}
- async def npm_build(
- self, working_directory_path: Path, assets_directory_path: Path
- ) -> None:
- await self.app.extensions[_Npm].install(type(self), working_directory_path)
- await npm(("run", "webpack"), cwd=working_directory_path)
- await self._copy_npm_build(
- working_directory_path / "webpack-build", assets_directory_path
- )
- logging.getLogger(__name__).info(
- self._app.localizer._("Built the Cotton Candy front-end assets.")
- )
-
- async def _copy_npm_build(
- self, source_directory_path: Path, destination_directory_path: Path
- ) -> None:
- await makedirs(destination_directory_path, exist_ok=True)
- await asyncio.to_thread(
- copy2,
- source_directory_path / "cotton_candy.css",
- destination_directory_path / "cotton_candy.css",
- )
- await asyncio.to_thread(
- copy2,
- source_directory_path / "cotton_candy.js",
- destination_directory_path / "cotton_candy.js",
- )
-
- async def generate(self, job_context: GenerationContext) -> None:
- assets_directory_path = await self.app.extensions[_Npm].ensure_assets(self)
- await makedirs(self.app.project.configuration.www_directory_path, exist_ok=True)
- await self._copy_npm_build(
- assets_directory_path, self.app.project.configuration.www_directory_path
- )
-
@pass_context
async def _global_search_index(context: Context) -> AsyncIterable[dict[str, str]]:
diff --git a/betty/extension/cotton_candy/assets/betty.extension.npm._Npm/.gitignore b/betty/extension/cotton_candy/assets/betty.extension.npm._Npm/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/betty/extension/cotton_candy/assets/betty.extension.npm._Npm/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/betty/extension/cotton_candy/assets/betty.extension.npm._Npm/src/main.ts b/betty/extension/cotton_candy/assets/betty.extension.npm._Npm/src/main.ts
deleted file mode 100644
index 9b17660e1..000000000
--- a/betty/extension/cotton_candy/assets/betty.extension.npm._Npm/src/main.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-'use strict'
-
-import './file.ts' // eslint-disable-line no-unused-vars
-import './search.ts' // eslint-disable-line no-unused-vars
-import './show.ts' // eslint-disable-line no-unused-vars
-import './main.scss' // eslint-disable-line @typescript-eslint/no-unused-vars
diff --git a/betty/extension/cotton_candy/assets/betty.extension.npm._Npm/src/webpack.config.js b/betty/extension/cotton_candy/assets/betty.extension.npm._Npm/src/webpack.config.js
deleted file mode 100644
index a598af181..000000000
--- a/betty/extension/cotton_candy/assets/betty.extension.npm._Npm/src/webpack.config.js
+++ /dev/null
@@ -1,98 +0,0 @@
-'use strict'
-
-import { CleanWebpackPlugin } from 'clean-webpack-plugin'
-import MiniCssExtractPlugin from 'mini-css-extract-plugin'
-import path from 'path'
-import { readFile } from 'node:fs/promises'
-import url from 'node:url'
-
-const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
-const configuration = JSON.parse(await readFile('./webpack.config.json'))
-
-export default {
- mode: configuration.debug ? 'development' : 'production',
- entry: {
- cotton_candy: path.resolve(__dirname, 'main.ts')
- },
- output: {
- path: path.resolve(__dirname, 'webpack-build'),
- filename: '[name].js'
- },
- optimization: {
- minimize: !configuration.debug,
- splitChunks: {
- cacheGroups: {
- styles: {
- name: 'cotton_candy',
- // Group all CSS files into a single file.
- test: /\.css$/,
- chunks: 'all',
- enforce: true
- }
- }
- }
- },
- plugins: [
- new CleanWebpackPlugin(),
- new MiniCssExtractPlugin({
- filename: '[name].css'
- })
- ],
- resolve: {
- extensions: ['', '.ts', '.js', '*']
- },
- module: {
- rules: [
- {
- test: /\.(s?css)$/,
- use: [
- {
- loader: MiniCssExtractPlugin.loader,
- options: {
- publicPath: '/'
- }
- },
- {
- loader: 'css-loader',
- options: {
- url: false
- }
- },
- {
- loader: 'postcss-loader',
- options: {
- postcssOptions: {
- plugins: () => [
- require('autoprefixer')
- ]
- }
- }
- },
- {
- loader: 'sass-loader'
- }
- ]
- },
- {
- test: /\.(js|ts)$/,
- exclude: /node_modules/,
- use: {
- loader: 'babel-loader',
- options: {
- presets: [
- [
- '@babel/preset-env', {
- debug: configuration.debug,
- useBuiltIns: 'usage',
- corejs: 3
- }
- ],
- '@babel/preset-typescript'
- ],
- cacheDirectory: configuration.cacheDirectory
- }
- }
- }
- ]
- }
-}
diff --git a/betty/extension/cotton_candy/assets/betty.extension.npm._Npm/src/webpack.config.json.j2 b/betty/extension/cotton_candy/assets/betty.extension.npm._Npm/src/webpack.config.json.j2
deleted file mode 100644
index 69af3962e..000000000
--- a/betty/extension/cotton_candy/assets/betty.extension.npm._Npm/src/webpack.config.json.j2
+++ /dev/null
@@ -1,4 +0,0 @@
-{{ {
- 'debug': app.project.configuration.debug,
- 'cacheDirectory': app.cache.with_scope(app.extensions['betty.extension.CottonCandy'].name()).with_scope(app.project.name).with_scope('babel').path | str,
-} | tojson }}
diff --git a/betty/extension/cotton_candy/assets/templates/base.html.j2 b/betty/extension/cotton_candy/assets/templates/base.html.j2
index e3f20b0b3..a07c1640d 100644
--- a/betty/extension/cotton_candy/assets/templates/base.html.j2
+++ b/betty/extension/cotton_candy/assets/templates/base.html.j2
@@ -4,8 +4,6 @@
{% include 'head.html.j2' %}
-
-
+