Skip to content

Commit

Permalink
Merge pull request #115 from amur-tiger/develop
Browse files Browse the repository at this point in the history
Release 0.8.3
  • Loading branch information
amur-tiger committed Apr 7, 2024
2 parents 89e11a1 + 7f74fcd commit 224ba42
Show file tree
Hide file tree
Showing 47 changed files with 1,309 additions and 746 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# FanFiction Enhancements

## 0.8.3

* Feature: Formatting dialog with more extensive options ([#79](https://github.com/amur-tiger/fanfiction-enhancements/issues/79))
* Fixed: Dark mode support for popups and modals ([#109](https://github.com/amur-tiger/fanfiction-enhancements/issues/109))
* Fixed: StoryCards not re-rendering when story updates

## 0.8.2

* Fixed: Added automatic re-authentication for sync with Google
Expand Down
486 changes: 321 additions & 165 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fanfiction-enhancements",
"version": "0.8.2",
"version": "0.8.3",
"author": "Arne 'TigeR' Linck",
"description": "FanFiction.net Enhancements",
"license": "MIT",
Expand Down Expand Up @@ -42,9 +42,11 @@
"@googleapis/drive": "^8.7.0",
"@types/babel__core": "^7.20.5",
"@types/greasemonkey": "^4.0.7",
"@types/jquery": "1.10.45",
"autoprefixer": "^10.4.18",
"esbuild": "^0.18.20",
"postcss": "^8.4.35",
"postcss-modules": "^6.0.0",
"postcss-nested": "^6.0.1",
"prettier": "^3.2.5",
"rimraf": "^5.0.5",
Expand Down
19 changes: 17 additions & 2 deletions scripts/gm-css-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Plugin } from "esbuild";
import postcss from "postcss";
import autoprefixer from "autoprefixer";
import postcssNested from "postcss-nested";
import postcssModules from "postcss-modules";

export default function gmCssLoader(): Plugin {
return {
Expand All @@ -28,13 +29,27 @@ export default function gmCssLoader(): Plugin {
};
}

const transformed = await postcss([autoprefixer, postcssNested]).process(content, {
let names = {};
const transformed = await postcss([
postcssModules({
localsConvention: "dashesOnly",
generateScopedName: "ffe-[local]_[hash:base64:5]",
getJSON(cssFileName, json, outputFileName) {
names = json;
},
}),
autoprefixer,
postcssNested,
]).process(content, {
map: false,
from: args.path,
});

return {
contents: `GM_addStyle(\`${transformed.css.replace(/`/g, "\\`")}\`)`,
contents: `
GM_addStyle(\`${transformed.css.replace(/`/g, "\\`")}\`);
export default ${JSON.stringify(names)};
`,
loader: "js",
};
});
Expand Down
156 changes: 79 additions & 77 deletions scripts/jsx-transform.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
import path from "node:path";
import fs from "node:fs/promises";
import type { Plugin } from "esbuild";
import babel, { type NodePath, type PluginObj } from "@babel/core";
import babel, { type NodePath, type PluginObj, type PluginPass } from "@babel/core";
import {
type ArrayExpression,
arrowFunctionExpression,
callExpression,
type CallExpression,
type Expression,
identifier,
type Identifier,
importDeclaration,
importDefaultSpecifier,
importSpecifier,
isArrayExpression,
isCallExpression,
isIdentifier,
isObjectExpression,
isObjectProperty,
isStringLiteral,
type Node,
type ObjectExpression,
stringLiteral,
} from "@babel/types";
import type { Scope } from "@babel/traverse";
// @ts-ignore
import presetTypescript from "@babel/preset-typescript";
// @ts-ignore
Expand Down Expand Up @@ -79,65 +78,66 @@ export default function jsxTransform(): Plugin {
}

function babelTransform(): PluginObj {
let renderVariableName = "";
let computeVariableName = "";
function isJsxCall(node: Node | null | undefined, pass: PluginPass): node is CallExpression {
const jsx = pass.get("@babel/plugin-react-jsx/id/jsx")();
const jsxs = pass.get("@babel/plugin-react-jsx/id/jsxs")();

function getRenderName(scope: Scope) {
if (!renderVariableName) {
renderVariableName = scope.generateUid("render");
if (!isIdentifier(jsx) || !isIdentifier(jsxs)) {
throw new TypeError("Could not determine jsx imported name.");
}
return renderVariableName;
}

function getComputeName(scope: Scope) {
if (!computeVariableName) {
computeVariableName = scope.generateUid("compute");
}
return computeVariableName;
return isCallExpression(node) && (isIdentifier(node.callee, jsx) || isIdentifier(node.callee, jsxs));
}

function isJsxCall(node: Node | null | undefined) {
return (
isCallExpression(node) &&
(isIdentifier(node.callee, { name: "_jsx" }) || isIdentifier(node.callee, { name: "_jsxs" }))
);
}
function hasSignalCall(path: NodePath, pass: PluginPass): boolean {
const options = {
pass,
hasCall: false,
};

function isReactiveCall(node: CallExpression) {
return (
(isIdentifier(node.callee, { name: renderVariableName }) ||
isIdentifier(node.callee, { name: computeVariableName })) &&
node.arguments.length === 1
);
}
path.traverse(
{
FunctionExpression(p) {
p.skip();
},

function hasSignalCall(path: NodePath, withJsx = false): boolean {
let hasCall = false;
let hasJsx = false;
ArrowFunctionExpression(p) {
p.skip();
},

path.traverse({
ArrowFunctionExpression(p) {
p.skip();
CallExpression(p, options) {
if (isJsxCall(p.node, options.pass) && !isStringLiteral(p.node.arguments[0])) {
p.skip();
} else if (p.node.arguments.length === 0) {
options.hasCall = true;
p.stop();
}
},
},
options,
);

CallExpression(p) {
if (isJsxCall(p.node)) {
p.skip();
hasJsx = true;
} else if (isReactiveCall(p.node)) {
p.skip();
} else {
p.stop();
hasCall = true;
}
},
});
return options.hasCall;
}

function isWrapped(path: NodePath, pass: PluginPass) {
if (path.parentPath == null || !path.parentPath.isArrowFunctionExpression()) {
return false;
}

const render = pass.get("ffe-transform/imports/render");
const compute = pass.get("ffe-transform/imports/compute");
const call = path.parentPath.parentPath.node;

return withJsx ? hasCall && hasJsx : hasCall;
return isCallExpression(call) && (isIdentifier(call.callee, render) || isIdentifier(call.callee, compute));
}

function wrapCall(path: NodePath<Expression>, func: string) {
path.replaceWith(callExpression(identifier(func), [arrowFunctionExpression([], path.node)]));
function wrapCall(path: NodePath<Expression>, func: Identifier, pass: PluginPass) {
if (isWrapped(path, pass)) {
return;
}

path.replaceWith(callExpression(func, [arrowFunctionExpression([], path.node)]));
path.skip();
}

Expand All @@ -154,36 +154,35 @@ function babelTransform(): PluginObj {
},

visitor: {
Program: {
exit(path) {
if (renderVariableName) {
path.node.body.push(
importDeclaration([importDefaultSpecifier(identifier(renderVariableName))], stringLiteral("@jsx/render")),
);
}
if (computeVariableName) {
path.node.body.push(
importDeclaration(
[importSpecifier(identifier(computeVariableName), identifier("compute"))],
stringLiteral("@jsx/render"),
),
);
}
},
Program(path, pass) {
const render = identifier(path.scope.generateUid("render"));
pass.set("ffe-transform/imports/render", render);

const compute = identifier(path.scope.generateUid("compute"));
pass.set("ffe-transform/imports/compute", compute);

path.node.body.unshift(
importDeclaration(
[importDefaultSpecifier(render), importSpecifier(compute, identifier("compute"))],
stringLiteral("@jsx/render"),
),
);
},

CallExpression: {
exit(path) {
if (!(isJsxCall(path.node) && isObjectExpression(path.node.arguments[1])) || isReactiveCall(path.node)) {
// skip if not jsx()-call, skip if jsx()-call was already wrapped
exit(path, pass) {
if (!isJsxCall(path.node, pass)) {
// this call is not jsx or already wrapped
return;
}

const isComponent = !isStringLiteral(path.node.arguments[0]);
const props = path.get("arguments")[1] as NodePath<ObjectExpression>;

for (const prop of props.get("properties")) {
if (
!isObjectProperty(prop.node) ||
(isCallExpression(prop.node.value) && isIdentifier(prop.node.value.callee, { name: computeVariableName }))
(isCallExpression(prop.node.value) && isJsxCall(prop.node.value.callee, pass))
) {
continue;
}
Expand All @@ -197,22 +196,25 @@ function babelTransform(): PluginObj {
// wrap each reactive child
if (isArrayExpression(value.node)) {
for (const item of (value as NodePath<ArrayExpression>).get("elements")) {
if (hasSignalCall(item as NodePath<Expression>)) {
wrapCall(item as NodePath<Expression>, getRenderName(item.scope));
if (hasSignalCall(item as NodePath<Expression>, pass)) {
wrapCall(item as NodePath<Expression>, pass.get("ffe-transform/imports/render"), pass);
}
}
} else {
if (hasSignalCall(value)) {
wrapCall(value as NodePath<Expression>, getRenderName(value.scope));
if (hasSignalCall(value, pass)) {
wrapCall(value as NodePath<Expression>, pass.get("ffe-transform/imports/render"), pass);
}
}
} else if (isStringLiteral(path.node.arguments[0])) {
} else if (!isComponent && hasSignalCall(prop, pass)) {
// for intrinsic elements, wrap attributes (components have to be re-rendered whole)
if (hasSignalCall(prop)) {
wrapCall(value as NodePath<Expression>, getComputeName(value.scope));
}
wrapCall(value as NodePath<Expression>, pass.get("ffe-transform/imports/compute"), pass);
}
}

if (isComponent) {
// always wrap components
wrapCall(path, pass.get("ffe-transform/imports/render"), pass);
}
},
},
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/Button/Button.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.btn > svg {
:global .btn > svg {
height: 19px;
vertical-align: text-bottom;
margin-top: -2px;
Expand Down
1 change: 0 additions & 1 deletion src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import clsx from "clsx";

import "./Button.css";

export interface ButtonProps {
Expand Down
18 changes: 9 additions & 9 deletions src/components/ChapterList/ChapterList.css
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
.ffe-cl-container {
.container {
margin-bottom: 50px;
padding: 20px;
}

.ffe-cl ol {
.list {
border-top: 1px solid var(--ffe-divider-color);
list-style-type: none;
margin: 0;
}

.ffe-cl-chapter {
.chapter {
background-color: var(--ffe-panel-color);
border-bottom: 1px solid var(--ffe-divider-color);
font-size: 1.1em;
line-height: 2em;
padding: 4px 20px;
}

.ffe-cl-chapter a {
color: var(--ffe-on-panel-link-color) !important;
a {
color: var(--ffe-on-panel-link-color) !important;
}
}

.ffe-cl-words {
.words {
color: var(--ffe-on-panel-color);
float: right;
font-size: 0.9em;
}

.ffe-cl-estimate {
.estimate {
color: var(--ffe-on-panel-color-faint);

&:before {
content: "~";
}
}

.ffe-cl-collapsed {
.collapsed {
text-align: center;

a {
Expand Down

0 comments on commit 224ba42

Please sign in to comment.