refactor: update js files to typescript and replace parcel with rollup

- add basic rollup config to bundle minified and browser compatible js
- use babel to transpile
typescript files to js
- add static code checkers: eslint and stylelint
- update package.json
scripts
- update DEPENDENCIES.md file to include rollup and popper
- set html in rss feed
description fields
- update Podcast and Episode entities to add description_html attribute
generated by parsing markdown to html using parsedown

#9
This commit is contained in:
Yassine Doghri 2020-07-28 15:57:48 +00:00
parent c0e66d5f70
commit e0da11517d
26 changed files with 1943 additions and 2801 deletions

4
.babelrc Normal file
View File

@ -0,0 +1,4 @@
{
"presets": ["@babel/preset-typescript", "@babel/preset-env"],
"plugins": ["@babel/plugin-proposal-class-properties"]
}

5
.browserslistrc Normal file
View File

@ -0,0 +1,5 @@
# Browsers that we support
>0.2%,
not dead,
not op_mini all

View File

@ -6,24 +6,22 @@
"settings": {
"terminal.integrated.shell.linux": "/bin/bash",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
},
"[php]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"color-highlight.markerType": "dot-before"
},
"extensions": [
"mikestead.dotenv",
"bmewburn.vscode-intelephense-client",
"streetsidesoftware.code-spell-checker",
"naumovs.color-highlight",
"heybourn.headwind",
"wayou.vscode-todo-highlight",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss",
"jamesbirtles.svelte-vscode",
"dbaeumer.vscode-eslint"
]
"mikestead.dotenv",
"bmewburn.vscode-intelephense-client",
"streetsidesoftware.code-spell-checker",
"naumovs.color-highlight",
"heybourn.headwind",
"wayou.vscode-todo-highlight",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss",
"jamesbirtles.svelte-vscode",
"dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint"
]
}

View File

@ -3,7 +3,14 @@
"browser": true,
"es2020": true
},
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
],
"parserOptions": {
"ecmaVersion": 11,
"sourceType": "module"

18
.stylelintrc.json Normal file
View File

@ -0,0 +1,18 @@
{
"extends": "stylelint-config-recommended",
"rules": {
"at-rule-no-unknown": [
true,
{
"ignoreAtRules": [
"tailwind",
"apply",
"responsive",
"variants",
"screen"
]
}
],
"no-descending-specificity": null
}
}

View File

@ -14,10 +14,11 @@ PHP Dependencies:
Javascript dependencies:
- [rollup](https://rollupjs.org/) ([MIT License](https://github.com/rollup/rollup/blob/master/LICENSE.md))
- [tailwindcss](https://tailwindcss.com/) ([MIT License](https://github.com/tailwindcss/tailwindcss/blob/master/LICENSE))
- [CodeMirror](https://github.com/codemirror/CodeMirror) ([MIT License](https://github.com/codemirror/CodeMirror/blob/master/LICENSE))
- [ProseMirror](https://prosemirror.net/) ([MIT License](https://github.com/ProseMirror/prosemirror/blob/master/LICENSE))
- [D3: Data-Driven Documents](https://github.com/d3/d3) ([BSD 3-Clause "New" or "Revised" License](https://github.com/d3/d3/blob/master/LICENSE))
- [D3: Data-Driven Documents](https://d3js.org) ([BSD 3-Clause "New" or "Revised" License](https://github.com/d3/d3/blob/master/LICENSE))
Other:

View File

@ -159,23 +159,19 @@ class Episode extends Entity
public function getDescriptionHtml()
{
$converter = new CommonMarkConverter([
'html_input' => 'strip',
'allow_unsafe_links' => false,
'renderer' => [
'soft_break' => '<br>',
],
]);
$converter = new Parsedown();
$converter->setBreaksEnabled(true);
if (
$description_footer = $this->getPodcast()
->episode_description_footer
) {
return $converter->convertToHtml(
$this->attributes['description'] . '---'
) . $converter->convertToHtml($description_footer);
return $converter->text($this->attributes['description']) .
'<footer>' .
$converter->text($description_footer) .
'</footer>';
}
return $converter->convertToHtml($this->attributes['description']);
return $converter->text($this->attributes['description']);
}
}

View File

@ -10,16 +10,18 @@ namespace App\Entities;
use App\Models\EpisodeModel;
use CodeIgniter\Entity;
use Myth\Auth\Models\UserModel;
use Parsedown;
class Podcast extends Entity
{
protected $link;
protected string $link;
protected \CodeIgniter\Files\File $image;
protected $image_media_path;
protected $image_url;
protected string $image_media_path;
protected string $image_url;
protected $episodes;
protected $owner;
protected \Myth\Auth\Entities\User $owner;
protected $contributors;
protected string $description_html;
protected $casts = [
'id' => 'integer',
@ -132,6 +134,11 @@ class Podcast extends Entity
return $this;
}
/**
* Returns all podcast contributors
*
* @return \Myth\Auth\Entities\User[]
*/
public function getContributors()
{
return (new UserModel())
@ -140,4 +147,12 @@ class Podcast extends Entity
->where('users_podcasts.podcast_id', $this->attributes['id'])
->findAll();
}
public function getDescriptionHtml()
{
$converter = new Parsedown();
$converter->setBreaksEnabled(true);
return $converter->text($this->attributes['description']);
}
}

View File

@ -53,7 +53,7 @@ function get_rss_feed($podcast)
$channel->addChild('docs', 'https://cyber.harvard.edu/rss/rss.html');
$channel->addChild('title', $podcast->title);
$channel->addChildWithCDATA('description', $podcast->description);
$channel->addChildWithCDATA('description', $podcast->description_html);
$itunes_image = $channel->addChild('image', null, $itunes_namespace);
$itunes_image->addAttribute('href', $podcast->image_url);
$channel->addChild('language', $podcast->language);
@ -124,7 +124,7 @@ function get_rss_feed($podcast)
$item->addChild('guid', $episode->guid);
$item->addChild('pubDate', $episode->pub_date->format(DATE_RFC1123));
$item->addChildWithCDATA('description', $episode->description);
$item->addChildWithCDATA('description', $episode->description_html);
$item->addChild(
'duration',
$enclosure_metadata['playtime_seconds'],

View File

@ -1 +0,0 @@
console.log("main");

View File

@ -1,56 +0,0 @@
import { createPopper } from "@popperjs/core";
const Dropdown = () => {
const dropdownContainers = document.querySelectorAll(
"[data-toggle='dropdown']"
);
for (let i = 0; i < dropdownContainers.length; i++) {
const dropdownContainer = dropdownContainers[i];
const button = dropdownContainer.querySelector("[data-popper='button']");
const menu = dropdownContainer.querySelector("[data-popper='menu']");
const popper = createPopper(button, menu, {
placement: menu.dataset.popperPlacement,
modifiers: [
{
name: "offset",
options: {
offset: [menu.dataset.popperOffsetX, menu.dataset.popperOffsetY],
},
},
],
});
const dropdownToggle = () => {
const isExpanded = !menu.classList.contains("hidden");
if (isExpanded) {
menu.classList.add("hidden");
menu.classList.remove("flex");
} else {
menu.classList.add("flex");
menu.classList.remove("hidden");
}
button.setAttribute("aria-expanded", isExpanded);
popper.update();
};
// Toggle dropdown menu on button click event
button.addEventListener("click", dropdownToggle);
// Toggle off when clicking outside of dropdown
document.addEventListener("click", function (event) {
const isExpanded = !menu.classList.contains("hidden");
const isClickOutside = !dropdownContainer.contains(event.target);
if (isExpanded && isClickOutside) {
dropdownToggle();
}
});
}
};
export default Dropdown;

View File

@ -0,0 +1,64 @@
import { createPopper, Placement } from "@popperjs/core";
const Dropdown = (): void => {
const dropdownContainers: NodeListOf<HTMLElement> = document.querySelectorAll(
"[data-toggle='dropdown']"
);
for (let i = 0; i < dropdownContainers.length; i++) {
const dropdownContainer = dropdownContainers[i];
const button: HTMLElement | null = dropdownContainer.querySelector(
"[data-popper='button']"
);
const menu: HTMLElement | null = dropdownContainer.querySelector(
"[data-popper='menu']"
);
if (button && menu) {
const popper = createPopper(button, menu, {
placement: menu.dataset.popperPlacement as Placement,
modifiers: [
{
name: "offset",
options: {
offset: [menu.dataset.popperOffsetX, menu.dataset.popperOffsetY],
},
},
],
});
const dropdownToggle = () => {
const isExpanded = !menu.classList.contains("hidden");
if (isExpanded) {
menu.classList.add("hidden");
menu.classList.remove("flex");
} else {
menu.classList.add("flex");
menu.classList.remove("hidden");
}
button.setAttribute("aria-expanded", isExpanded.toString());
popper.update();
};
// Toggle dropdown menu on button click event
button.addEventListener("click", dropdownToggle);
// Toggle off when clicking outside of dropdown
document.addEventListener("click", function (event) {
const isExpanded = !menu.classList.contains("hidden");
const isClickOutside = !dropdownContainer.contains(
event.target as Node
);
if (isExpanded && isClickOutside) {
dropdownToggle();
}
});
}
}
};
export default Dropdown;

View File

@ -1,8 +1,8 @@
import CodeMirror from "codemirror";
import "codemirror/lib/codemirror.css";
const HTMLEditor = () => {
const allHTMLEditors = document.querySelectorAll(
const HTMLEditor = (): void => {
const allHTMLEditors: NodeListOf<HTMLTextAreaElement> = document.querySelectorAll(
"textarea[data-editor='html']"
);

View File

@ -11,7 +11,9 @@ import { EditorView } from "prosemirror-view";
import "prosemirror-view/style/prosemirror.css";
class MarkdownView {
constructor(target) {
textarea: HTMLTextAreaElement;
constructor(target: HTMLTextAreaElement) {
this.textarea = target;
this.textarea.classList.add("w-full", "h-full");
}
@ -31,16 +33,20 @@ class MarkdownView {
}
class ProseMirrorView {
constructor(target, content) {
editorContainer: HTMLDivElement;
view: EditorView;
constructor(target: HTMLTextAreaElement, content: string) {
this.editorContainer = document.createElement("div");
this.editorContainer.classList.add(
"bg-white",
"border",
"px-2",
"min-h-full"
"min-h-full",
"prose-sm"
);
this.editorContainer.style.minHeight = "200px";
const editor = target.parentNode.insertBefore(
const editor = target.parentNode?.insertBefore(
this.editorContainer,
target.nextSibling
);
@ -51,7 +57,7 @@ class ProseMirrorView {
plugins: exampleSetup({ schema }),
}),
dispatchTransaction: (transaction) => {
let newState = this.view.state.apply(transaction);
const newState = this.view.state.apply(transaction);
this.view.updateState(newState);
if (transaction.docChanged) {
@ -75,16 +81,18 @@ class ProseMirrorView {
}
}
const MarkdownEditor = () => {
const targets = document.querySelectorAll("textarea[data-editor='markdown']");
const activeClass = ["font-bold"];
const MarkdownEditor = (): void => {
const targets: NodeListOf<HTMLTextAreaElement> = document.querySelectorAll(
"textarea[data-editor='markdown']"
);
const activeClass = "font-bold";
for (let i = 0; i < targets.length; i++) {
const target = targets[i];
const wysiwygBtn = document.createElement("button");
wysiwygBtn.classList.add(
...activeClass,
activeClass,
"py-1",
"px-2",
"bg-white",
@ -112,7 +120,7 @@ const MarkdownEditor = () => {
const markdownEditorContainer = document.createElement("div");
markdownEditorContainer.classList.add("relative");
markdownEditorContainer.style.minHeight = "200px";
target.parentNode.appendChild(markdownEditorContainer);
target.parentNode?.appendChild(markdownEditorContainer);
markdownEditorContainer.appendChild(target);
// show WYSIWYG editor by default
@ -123,17 +131,17 @@ const MarkdownEditor = () => {
markdownEditorContainer.appendChild(viewButtons);
markdownBtn.addEventListener("click", () => {
if (markdownBtn.classList.contains(...activeClass)) return;
markdownBtn.classList.add(...activeClass);
wysiwygBtn.classList.remove(...activeClass);
if (markdownBtn.classList.contains(activeClass)) return;
markdownBtn.classList.add(activeClass);
wysiwygBtn.classList.remove(activeClass);
wysiwygView.hide();
markdownView.show();
});
wysiwygBtn.addEventListener("click", () => {
if (wysiwygBtn.classList.contains(...activeClass)) return;
wysiwygBtn.classList.add(...activeClass);
markdownBtn.classList.remove(...activeClass);
if (wysiwygBtn.classList.contains(activeClass)) return;
wysiwygBtn.classList.add(activeClass);
markdownBtn.classList.remove(activeClass);
markdownView.hide();
wysiwygView.show();
});

View File

@ -1,12 +1,12 @@
// Original code from: https://gist.github.com/hagemann/382adfc57adbd5af078dc93feef01fe1
const slugify = (string) => {
const slugify = (text: string) => {
const a =
"àáâäæãåāăąçćčđďèéêëēėęěğǵḧîïíīįìłḿñńǹňôöòóœøōõőṕŕřßśšşșťțûüùúūǘůűųẃẍÿýžźż·/_,:;";
const b =
"aaaaaaaaaacccddeeeeeeeegghiiiiiilmnnnnoooooooooprrsssssttuuuuuuuuuwxyyzzz------";
const p = new RegExp(a.split("").join("|"), "g");
return string
return text
.toString()
.toLowerCase()
.replace(/\s+/g, "-") // Replace spaces with -
@ -18,9 +18,13 @@ const slugify = (string) => {
.replace(/-+$/, ""); // Trim - from end of text
};
const Slugify = () => {
const title = document.querySelector("input[data-slugify='title']");
const slug = document.querySelector("input[data-slugify='slug']");
const Slugify = (): void => {
const title: HTMLInputElement | null = document.querySelector(
"input[data-slugify='title']"
);
const slug: HTMLInputElement | null = document.querySelector(
"input[data-slugify='slug']"
);
if (title && slug) {
title.addEventListener("input", () => {

View File

@ -1,7 +1,7 @@
import { createPopper } from "@popperjs/core";
import { createPopper, Placement } from "@popperjs/core";
const Tooltip = () => {
const tooltipContainers = document.querySelectorAll(
const Tooltip = (): void => {
const tooltipContainers: NodeListOf<HTMLElement> = document.querySelectorAll(
"[data-toggle='tooltip']"
);
@ -18,7 +18,7 @@ const Tooltip = () => {
tooltip.innerHTML = tooltipContent;
const popper = createPopper(tooltipReference, tooltip, {
placement: tooltipReference.dataset.placement,
placement: tooltipReference.dataset.placement as Placement,
modifiers: [
{
name: "offset",

2
app/Views/_assets/typings.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
declare module "prosemirror-markdown";
declare module "prosemirror-example-setup";

View File

@ -8,6 +8,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
<link rel="stylesheet" href="/assets/index.css"/>
<?php if (isset($podcast)): ?>
<?= $podcast->custom_html_head ?>
<?php endif; ?>
</head>
<body class="flex flex-col min-h-screen mx-auto">

View File

@ -11,7 +11,7 @@
"whichbrowser/parser": "^2.0",
"geoip2/geoip2": "~2.0",
"myth/auth": "1.0-beta.2",
"league/commonmark": "^1.5"
"erusev/parsedown": "^1.7"
},
"require-dev": {
"mikey179/vfsstream": "1.6.*",

143
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "df18ba9c8ecbb43a37d2a90ebd4316f6",
"content-hash": "3656eaed72238d7b46af985ec86f6533",
"packages": [
{
"name": "codeigniter4/framework",
@ -116,6 +116,52 @@
],
"time": "2020-04-08T08:27:21+00:00"
},
{
"name": "erusev/parsedown",
"version": "1.7.4",
"source": {
"type": "git",
"url": "https://github.com/erusev/parsedown.git",
"reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
"reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35"
},
"type": "library",
"autoload": {
"psr-0": {
"Parsedown": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Emanuil Rusev",
"email": "hello@erusev.com",
"homepage": "http://erusev.com"
}
],
"description": "Parser for Markdown.",
"homepage": "http://parsedown.org",
"keywords": [
"markdown",
"parser"
],
"time": "2019-12-30T22:54:17+00:00"
},
{
"name": "geoip2/geoip2",
"version": "v2.10.0",
@ -427,101 +473,6 @@
],
"time": "2020-05-20T16:45:56+00:00"
},
{
"name": "league/commonmark",
"version": "1.5.3",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
"reference": "2574454b97e4103dc4e36917bd783b25624aefcd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/2574454b97e4103dc4e36917bd783b25624aefcd",
"reference": "2574454b97e4103dc4e36917bd783b25624aefcd",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": "^7.1 || ^8.0"
},
"conflict": {
"scrutinizer/ocular": "1.7.*"
},
"require-dev": {
"cebe/markdown": "~1.0",
"commonmark/commonmark.js": "0.29.1",
"erusev/parsedown": "~1.0",
"ext-json": "*",
"github/gfm": "0.29.0",
"michelf/php-markdown": "~1.4",
"mikehaertl/php-shellcommand": "^1.4",
"phpstan/phpstan": "^0.12",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.2",
"scrutinizer/ocular": "^1.5",
"symfony/finder": "^4.2"
},
"bin": [
"bin/commonmark"
],
"type": "library",
"autoload": {
"psr-4": {
"League\\CommonMark\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Colin O'Dell",
"email": "colinodell@gmail.com",
"homepage": "https://www.colinodell.com",
"role": "Lead Developer"
}
],
"description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and Github-Flavored Markdown (GFM)",
"homepage": "https://commonmark.thephpleague.com",
"keywords": [
"commonmark",
"flavored",
"gfm",
"github",
"github-flavored",
"markdown",
"md",
"parser"
],
"funding": [
{
"url": "https://enjoy.gitstore.app/repositories/thephpleague/commonmark",
"type": "custom"
},
{
"url": "https://www.colinodell.com/sponsor",
"type": "custom"
},
{
"url": "https://www.paypal.me/colinpodell/10.00",
"type": "custom"
},
{
"url": "https://github.com/colinodell",
"type": "github"
},
{
"url": "https://www.patreon.com/colinodell",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/league/commonmark",
"type": "tidelift"
}
],
"time": "2020-07-19T22:47:30+00:00"
},
{
"name": "maxmind-db/reader",
"version": "v1.6.0",

View File

@ -60,12 +60,15 @@ docker-compose run --rm composer install --ignore-platform-reqs
docker-compose run --rm node npm install
```
6. Build styles using postcss
6. Build assets: javascript, styles, icons and svg images
> To generate the `public/index.css` file, you must run the following command.
> To generate public assets, you must run the following commands.
```bash
docker-compose run --rm node npm run build:js
docker-compose run --rm node npm run build:css
docker-compose run --rm node npm run build:icons
docker-compose run --rm node npm run build:svg
```
## Start docker containers

4175
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,30 +9,47 @@
"url": "https://code.podlibre.org/podlibre/castopod.git"
},
"scripts": {
"watch:js": "parcel watch app/Views/_assets/*.js --out-dir public/assets",
"build:js": "parcel build app/Views/_assets/*.js --out-dir public/assets",
"build": "npm run build:js && cross-env NODE_ENV=production npm run build:css && npm run build:icons && npm run build:svg",
"watch:js": "rollup --config --watch",
"build:js": "rollup --config",
"watch:css": "postcss app/Views/_assets/styles/index.css -o public/assets/index.css -w",
"build:css": "postcss app/Views/_assets/styles/index.css -o public/assets/index.css",
"build:icons": "svgo -f app/Views/_assets/icons -o public/assets/icons --config=./.svgo.icons.yml",
"build:svg": "svgo -f app/Views/_assets/images -o public/assets/images --config=./.svgo.yml",
"build": "npm run build:js && cross-env NODE_ENV=production npm run build:css && npm run build:icons && npm run build:svg",
"lint": "eslint --ext js,ts app/Views/_assets",
"lint:fix": "eslint --ext js,ts app/Views/_assets --fix",
"lint:css": "stylelint \"app/Views/_assets/**/*.css\"",
"typecheck": "tsc",
"commit": "git-cz"
},
"dependencies": {
"@popperjs/core": "^2.4.4",
"codemirror": "^5.55.0",
"easymde": "^2.11.0",
"prosemirror-example-setup": "^1.1.2",
"prosemirror-markdown": "^1.5.0",
"prosemirror-state": "^1.3.3",
"prosemirror-view": "^1.15.2"
},
"devDependencies": {
"@babel/core": "^7.10.5",
"@babel/plugin-proposal-class-properties": "^7.10.4",
"@babel/preset-env": "^7.10.4",
"@babel/preset-typescript": "^7.10.4",
"@commitlint/cli": "^9.0.1",
"@commitlint/config-conventional": "^9.0.1",
"@prettier/plugin-php": "^0.14.2",
"@rollup/plugin-babel": "^5.1.0",
"@rollup/plugin-commonjs": "^14.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-multi-entry": "^3.0.1",
"@rollup/plugin-node-resolve": "^8.4.0",
"@tailwindcss/custom-forms": "^0.2.1",
"@tailwindcss/typography": "^0.2.0",
"@types/codemirror": "0.0.97",
"@types/prosemirror-markdown": "^1.0.3",
"@types/prosemirror-view": "^1.15.0",
"@typescript-eslint/eslint-plugin": "^3.7.0",
"@typescript-eslint/parser": "^3.7.0",
"cross-env": "^7.0.2",
"cssnano": "^4.1.10",
"cz-conventional-changelog": "^3.2.0",
@ -41,19 +58,22 @@
"eslint-plugin-prettier": "^3.1.4",
"husky": "^4.2.5",
"lint-staged": "^10.2.11",
"parcel-bundler": "^1.12.4",
"postcss-cli": "^7.1.1",
"postcss-import": "^12.0.1",
"postcss-preset-env": "^6.7.0",
"prettier": "2.0.5",
"prettier-plugin-organize-imports": "^1.1.1",
"rollup": "^2.23.0",
"rollup-plugin-multi-input": "^1.1.1",
"rollup-plugin-node-polyfills": "^0.2.1",
"rollup-plugin-postcss": "^3.1.3",
"rollup-plugin-terser": "^6.1.0",
"stylelint": "^13.6.1",
"stylelint-config-standard": "^20.0.0",
"svgo": "^1.3.2",
"tailwindcss": "^1.4.6"
"tailwindcss": "^1.4.6",
"typescript": "^3.9.7"
},
"browserslist": [
">0.2%",
"not dead",
"not op_mini all"
],
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
@ -61,7 +81,9 @@
}
},
"lint-staged": {
"*.{js,css,md,php}": "prettier --write"
"*.{js,ts,css,md,json,php}": "prettier --write",
"*.{ts,js}": "eslint --ext js,ts,tsx app/Views/_assets --fix",
"*.css": "stylelint --fix"
},
"config": {
"commitizen": {

44
rollup.config.js Normal file
View File

@ -0,0 +1,44 @@
import babel from "@rollup/plugin-babel";
import commonjs from "@rollup/plugin-commonjs";
import json from "@rollup/plugin-json";
import resolve from "@rollup/plugin-node-resolve";
import multiInput from "rollup-plugin-multi-input";
import nodePolyfills from "rollup-plugin-node-polyfills";
import postcss from "rollup-plugin-postcss";
import { terser } from "rollup-plugin-terser";
const INPUT_DIR = "app/Views/_assets";
const OUTPUT_DIR = "public/assets";
export default {
input: [`${INPUT_DIR}/*.ts`, `!${INPUT_DIR}/*.d.ts`],
output: {
dir: OUTPUT_DIR,
format: "esm",
sourcemap: true,
},
plugins: [
multiInput({ relative: INPUT_DIR }),
resolve({
preferBuiltins: false,
extensions: [".js", ".ts"],
}),
commonjs(),
postcss({ extract: true, sourceMap: true, minimize: true }),
json(),
nodePolyfills(),
babel({
babelHelpers: "bundled",
extensions: [".js", ".ts"],
exclude: "node_modules/**",
}),
terser(),
],
watch: {
chokidar: {
usePolling: true,
},
include: `${INPUT_DIR}/**/*.ts`,
exclude: `${INPUT_DIR}/**/*.d.ts`,
},
};

25
tsconfig.json Normal file
View File

@ -0,0 +1,25 @@
{
"compilerOptions": {
/* Basic Options */
"target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
"lib": [
"DOM",
"DOM.Iterable",
"ESNext"
] /* Specify library files to be included in the compilation. */,
"allowJs": true /* Allow javascript files to be compiled. */,
"noEmit": true /* Do not emit outputs. */,
/* Strict Type-Checking Options */
"strict": true /* Enable all strict type-checking options. */,
/* Module Resolution Options */
"baseUrl": "./app/Views/_assets" /* Base directory to resolve non-absolute module names. */,
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
/* Advanced Options */
"skipLibCheck": true /* Skip type checking of declaration files. */,
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}