mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-08-09 23:45:25 +02:00
Merge branch 'main' into fix-horizontal-scroll
This commit is contained in:
commit
82e01bc656
55 changed files with 2060 additions and 25059 deletions
23626
package-lock.json
generated
23626
package-lock.json
generated
File diff suppressed because it is too large
Load diff
18
package.json
18
package.json
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"name": "codex.docs",
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"browserslist": [
|
||||
"last 2 versions",
|
||||
"> 1%"
|
||||
|
@ -12,11 +13,12 @@
|
|||
"build-frontend": "webpack --mode=production",
|
||||
"build-frontend:dev": "webpack --mode=development --watch",
|
||||
"test:js": "cross-env NODE_ENV=testing mocha --recursive ./dist/test --exit",
|
||||
"test": "cross-env NODE_ENV=testing ts-mocha ./src/test/*.ts ./src/test/**/*.ts --exit",
|
||||
"test": "cross-env NODE_ENV=testing ts-mocha -n loader=ts-node/esm ./src/test/*.ts ./src/test/**/*.ts --exit ",
|
||||
"lint": "eslint --fix --ext .ts ./src/backend",
|
||||
"editor-upgrade": "yarn add -D @editorjs/{editorjs,header,code,delimiter,list,link,image,table,inline-code,marker,warning,checklist,raw}@latest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codexteam/shortcuts": "^1.2.0",
|
||||
"@hawk.so/javascript": "^3.0.1",
|
||||
"@hawk.so/nodejs": "^3.1.2",
|
||||
"config": "^3.3.6",
|
||||
|
@ -34,7 +36,7 @@
|
|||
"multer": "^1.4.2",
|
||||
"nedb": "^1.8.0",
|
||||
"node-cache": "^5.1.2",
|
||||
"node-fetch": "^2.6.1",
|
||||
"node-fetch": "^3.2.10",
|
||||
"open-graph-scraper": "^4.9.0",
|
||||
"twig": "^1.15.4",
|
||||
"uuid4": "^2.0.2"
|
||||
|
@ -48,7 +50,7 @@
|
|||
"@editorjs/checklist": "^1.3.0",
|
||||
"@editorjs/code": "^2.7.0",
|
||||
"@editorjs/delimiter": "^1.2.0",
|
||||
"@editorjs/editorjs": "^2.23.2",
|
||||
"@editorjs/editorjs": "^2.25.0",
|
||||
"@editorjs/embed": "^2.5.1",
|
||||
"@editorjs/header": "^2.6.2",
|
||||
"@editorjs/image": "^2.6.2",
|
||||
|
@ -98,7 +100,7 @@
|
|||
"eslint-plugin-node": "^11.1.0",
|
||||
"highlight.js": "^11.1.0",
|
||||
"mini-css-extract-plugin": "^2.6.0",
|
||||
"mocha": "^5.2.0",
|
||||
"mocha": "^10.0.0",
|
||||
"mocha-sinon": "^2.1.2",
|
||||
"module-dispatcher": "^2.0.0",
|
||||
"normalize.css": "^8.0.1",
|
||||
|
@ -118,10 +120,10 @@
|
|||
"postcss-nesting": "^10.1.3",
|
||||
"postcss-smart-import": "^0.7.6",
|
||||
"rimraf": "^3.0.2",
|
||||
"sinon": "^11.1.2",
|
||||
"ts-mocha": "^8.0.0",
|
||||
"ts-node": "^10.1.0",
|
||||
"typescript": "^4.3.5",
|
||||
"sinon": "^14.0.0",
|
||||
"ts-mocha": "^10.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.7.4",
|
||||
"webpack": "^5.70.0",
|
||||
"webpack-cli": "^4.9.2"
|
||||
}
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
import express, { NextFunction, Request, Response } from 'express';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import morgan from 'morgan';
|
||||
import rcParser from './utils/rcparser';
|
||||
import routes from './routes';
|
||||
import HttpException from './exceptions/httpException';
|
||||
import rcParser from './utils/rcparser.js';
|
||||
import routes from './routes/index.js';
|
||||
import HttpException from './exceptions/httpException.js';
|
||||
import * as dotenv from 'dotenv';
|
||||
import config from 'config';
|
||||
import HawkCatcher from '@hawk.so/nodejs';
|
||||
import { default as HawkCatcher } from '@hawk.so/nodejs';
|
||||
import os from 'os';
|
||||
import appConfig from 'config';
|
||||
import { downloadFavicon, FaviconData } from './utils/downloadFavicon';
|
||||
import { downloadFavicon, FaviconData } from './utils/downloadFavicon.js';
|
||||
|
||||
/**
|
||||
* The __dirname CommonJS variables are not available in ES modules.
|
||||
* https://nodejs.org/api/esm.html#no-__filename-or-__dirname
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
dotenv.config();
|
||||
const app = express();
|
||||
|
@ -34,7 +41,7 @@ if (process.env.HAWK_TOKEN_CLIENT) {
|
|||
// view engine setup
|
||||
app.set('views', path.join(__dirname, './', 'views'));
|
||||
app.set('view engine', 'twig');
|
||||
require('./utils/twig');
|
||||
import('./utils/twig.js');
|
||||
|
||||
const downloadedFaviconFolder = os.tmpdir();
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import Alias from '../models/alias';
|
||||
import Alias from '../models/alias.js';
|
||||
|
||||
/**
|
||||
* @class Aliases
|
||||
|
@ -14,10 +14,6 @@ class Aliases {
|
|||
public static async get(aliasName: string): Promise<Alias> {
|
||||
const alias = await Alias.get(aliasName);
|
||||
|
||||
if (!alias.id) {
|
||||
throw new Error('Entity with given alias does not exist');
|
||||
}
|
||||
|
||||
return alias;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import Page, { PageData } from '../models/page';
|
||||
import Alias from '../models/alias';
|
||||
import PagesOrder from './pagesOrder';
|
||||
import PageOrder from '../models/pageOrder';
|
||||
import HttpException from '../exceptions/httpException';
|
||||
import PagesFlatArray from '../models/pagesFlatArray';
|
||||
import Page, { PageData } from '../models/page.js';
|
||||
import Alias from '../models/alias.js';
|
||||
import PagesOrder from './pagesOrder.js';
|
||||
import PageOrder from '../models/pageOrder.js';
|
||||
import HttpException from '../exceptions/httpException.js';
|
||||
import PagesFlatArray from '../models/pagesFlatArray.js';
|
||||
|
||||
type PageDataFields = keyof PageData;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import PageOrder from '../models/pageOrder';
|
||||
import Page from '../models/page';
|
||||
import PagesFlatArray from '../models/pagesFlatArray';
|
||||
import PageOrder from '../models/pageOrder.js';
|
||||
import Page from '../models/page.js';
|
||||
import PagesFlatArray from '../models/pagesFlatArray.js';
|
||||
|
||||
/**
|
||||
* @class PagesOrder
|
||||
|
|
|
@ -3,9 +3,9 @@ import fetch from 'node-fetch';
|
|||
import fs from 'fs';
|
||||
import nodePath from 'path';
|
||||
import config from 'config';
|
||||
import File, { FileData } from '../models/file';
|
||||
import crypto from '../utils/crypto';
|
||||
import deepMerge from '../utils/objects';
|
||||
import File, { FileData } from '../models/file.js';
|
||||
import crypto from '../utils/crypto.js';
|
||||
import deepMerge from '../utils/objects.js';
|
||||
|
||||
const random16 = crypto.random16;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import crypto from '../utils/crypto';
|
||||
import database from '../utils/database/index';
|
||||
import crypto from '../utils/crypto.js';
|
||||
import database from '../utils/database/index.js';
|
||||
|
||||
const binaryMD5 = crypto.binaryMD5;
|
||||
const aliasesDb = database['aliases'];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import database from '../utils/database/index';
|
||||
import database from '../utils/database/index.js';
|
||||
|
||||
const filesDb = database['files'];
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import urlify from '../utils/urlify';
|
||||
import database from '../utils/database/index';
|
||||
import urlify from '../utils/urlify.js';
|
||||
import database from '../utils/database/index.js';
|
||||
|
||||
const pagesDb = database['pages'];
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import database from '../utils/database/index';
|
||||
import database from '../utils/database/index.js';
|
||||
|
||||
const db = database['pagesOrder'];
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Page from './page';
|
||||
import PageOrder from './pageOrder';
|
||||
import Page from './page.js';
|
||||
import PageOrder from './pageOrder.js';
|
||||
import NodeCache from 'node-cache';
|
||||
|
||||
// Create cache for flat array
|
||||
|
@ -43,7 +43,7 @@ export interface PagesFlatArrayData {
|
|||
}
|
||||
|
||||
/**
|
||||
* @class PagesFlatArray model - flat array of pages, which are ordered like in sidebar
|
||||
* @class PagesFlatArray model - flat array of pages, which are ordered like in sidebar
|
||||
*/
|
||||
class PagesFlatArray {
|
||||
/**
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import express, { Request, Response } from 'express';
|
||||
import Aliases from '../controllers/aliases';
|
||||
import Pages from '../controllers/pages';
|
||||
import Alias from '../models/alias';
|
||||
import verifyToken from './middlewares/token';
|
||||
import PagesFlatArray from '../models/pagesFlatArray';
|
||||
import Aliases from '../controllers/aliases.js';
|
||||
import Pages from '../controllers/pages.js';
|
||||
import Alias from '../models/alias.js';
|
||||
import verifyToken from './middlewares/token.js';
|
||||
import PagesFlatArray from '../models/pagesFlatArray.js';
|
||||
import HttpException from '../exceptions/httpException.js';
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
|
@ -24,7 +26,7 @@ router.get('*', verifyToken, async (req: Request, res: Response) => {
|
|||
const alias = await Aliases.get(url);
|
||||
|
||||
if (alias.id === undefined) {
|
||||
throw new Error('Alias not found');
|
||||
throw new HttpException(404, 'Alias not found');
|
||||
}
|
||||
|
||||
switch (alias.type) {
|
||||
|
@ -46,10 +48,17 @@ router.get('*', verifyToken, async (req: Request, res: Response) => {
|
|||
}
|
||||
}
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err,
|
||||
});
|
||||
if (err instanceof HttpException && (err as HttpException).status === 404) {
|
||||
res.status(404).render('error', {
|
||||
message: 'Page not found',
|
||||
status: 404,
|
||||
});
|
||||
} else {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: err,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import express from 'express';
|
||||
import pagesAPI from './pages';
|
||||
import transportAPI from './transport';
|
||||
import linksAPI from './links';
|
||||
import pagesAPI from './pages.js';
|
||||
import transportAPI from './transport.js';
|
||||
import linksAPI from './links.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import express, { Request, Response } from 'express';
|
||||
import multerFunc from 'multer';
|
||||
import Pages from '../../controllers/pages';
|
||||
import PagesOrder from '../../controllers/pagesOrder';
|
||||
import Pages from '../../controllers/pages.js';
|
||||
import PagesOrder from '../../controllers/pagesOrder.js';
|
||||
|
||||
const router = express.Router();
|
||||
const multer = multerFunc();
|
||||
|
|
|
@ -3,8 +3,8 @@ import multer, { StorageEngine } from 'multer';
|
|||
import mime from 'mime';
|
||||
import mkdirp from 'mkdirp';
|
||||
import config from 'config';
|
||||
import Transport from '../../controllers/transport';
|
||||
import { random16 } from '../../utils/crypto';
|
||||
import Transport from '../../controllers/transport.js';
|
||||
import { random16 } from '../../utils/crypto.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import express, { Request, Response } from 'express';
|
||||
import verifyToken from './middlewares/token';
|
||||
import verifyToken from './middlewares/token.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import express from 'express';
|
||||
import home from './home';
|
||||
import pages from './pages';
|
||||
import auth from './auth';
|
||||
import aliases from './aliases';
|
||||
import api from './api';
|
||||
import pagesMiddleware from './middlewares/pages';
|
||||
import home from './home.js';
|
||||
import pages from './pages.js';
|
||||
import auth from './auth.js';
|
||||
import aliases from './aliases.js';
|
||||
import api from './api/index.js';
|
||||
import pagesMiddleware from './middlewares/pages.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { NextFunction, Request, Response } from 'express';
|
||||
import Pages from '../../controllers/pages';
|
||||
import PagesOrder from '../../controllers/pagesOrder';
|
||||
import Page from '../../models/page';
|
||||
import asyncMiddleware from '../../utils/asyncMiddleware';
|
||||
import PageOrder from '../../models/pageOrder';
|
||||
import Pages from '../../controllers/pages.js';
|
||||
import PagesOrder from '../../controllers/pagesOrder.js';
|
||||
import Page from '../../models/page.js';
|
||||
import asyncMiddleware from '../../utils/asyncMiddleware.js';
|
||||
import PageOrder from '../../models/pageOrder.js';
|
||||
|
||||
/**
|
||||
* Process one-level pages list to parent-children list
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import express, { NextFunction, Request, Response } from 'express';
|
||||
import Pages from '../controllers/pages';
|
||||
import PagesOrder from '../controllers/pagesOrder';
|
||||
import verifyToken from './middlewares/token';
|
||||
import allowEdit from './middlewares/locals';
|
||||
import PagesFlatArray from '../models/pagesFlatArray';
|
||||
import Pages from '../controllers/pages.js';
|
||||
import PagesOrder from '../controllers/pagesOrder.js';
|
||||
import verifyToken from './middlewares/token.js';
|
||||
import allowEdit from './middlewares/locals.js';
|
||||
import PagesFlatArray from '../models/pagesFlatArray.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import Datastore from 'nedb';
|
||||
import { AliasData } from '../../models/alias';
|
||||
import { FileData } from '../../models/file';
|
||||
import { PageData } from '../../models/page';
|
||||
import { PageOrderData } from '../../models/pageOrder';
|
||||
import initDb from './initDb';
|
||||
import { AliasData } from '../../models/alias.js';
|
||||
import { FileData } from '../../models/file.js';
|
||||
import { PageData } from '../../models/page.js';
|
||||
import { PageOrderData } from '../../models/pageOrder.js';
|
||||
import initDb from './initDb.js';
|
||||
|
||||
/**
|
||||
* @typedef Options - optional params
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import fetch from 'node-fetch';
|
||||
import fetch, { RequestInit } from 'node-fetch';
|
||||
|
||||
/**
|
||||
* Uploaded favicon data
|
||||
|
@ -61,7 +61,7 @@ export async function downloadFavicon(destination: string, faviconFolder: string
|
|||
}, 5000);
|
||||
|
||||
// Make get request to url
|
||||
const res = await fetch(destination, { signal: controller.signal });
|
||||
const res = await fetch(destination, { signal: controller.signal as RequestInit['signal'] });
|
||||
// Get buffer data from response
|
||||
const fileData = await res.buffer();
|
||||
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import config from 'config';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
/**
|
||||
* The __dirname CommonJS variables are not available in ES modules.
|
||||
* https://nodejs.org/api/esm.html#no-__filename-or-__dirname
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const rcPath = path.resolve(__dirname, '../../../', config.get('rcFile') || './.codexdocsrc');
|
||||
|
||||
|
|
|
@ -3,8 +3,16 @@
|
|||
*/
|
||||
import twig from 'twig';
|
||||
import fs from 'fs';
|
||||
import urlify from './urlify';
|
||||
import urlify from './urlify.js';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
/**
|
||||
* The __dirname CommonJS variables are not available in ES modules.
|
||||
* https://nodejs.org/api/esm.html#no-__filename-or-__dirname
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
export default (function () {
|
||||
'use strict';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import translateString from './translation';
|
||||
import translateString from './translation.js';
|
||||
|
||||
/**
|
||||
* Convert text to URL-like string
|
||||
|
|
|
@ -52,4 +52,8 @@
|
|||
</div>
|
||||
|
||||
</aside>
|
||||
|
||||
<div class="docs-sidebar__slider">
|
||||
{{ svg('arrow-left') }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
{% extends 'layout.twig' %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{{message}}</h1>
|
||||
<h2>{{error.status}}</h2>
|
||||
<pre>{{error.stack}}</pre>
|
||||
<div class="error-page">
|
||||
<h1>
|
||||
┬┴┬┴┤ {{status}} ├┬┴┬┴
|
||||
</h1>
|
||||
<h1>{{message}}</h1>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -3,19 +3,22 @@
|
|||
{% block body %}
|
||||
<article class="page" data-module="page">
|
||||
<header class="page__header">
|
||||
<a href="/" class="page__header-nav">
|
||||
Documentation
|
||||
</a>
|
||||
{% if page._parent %}
|
||||
<a class="page__header-nav"
|
||||
{% if pageParent.uri %}
|
||||
href="/{{ pageParent.uri }}"
|
||||
{% else %}
|
||||
href="/page/{{ pageParent._id }}"
|
||||
{% endif %}>
|
||||
{{ pageParent.title }}
|
||||
<div class="page__header-nav">
|
||||
<a href="/" class="page__header-nav-item">
|
||||
Documentation
|
||||
</a>
|
||||
{% endif %}
|
||||
{{ svg('arrow-right') }}
|
||||
{% if page._parent %}
|
||||
<a class="page__header-nav-item"
|
||||
{% if pageParent.uri %}
|
||||
href="/{{ pageParent.uri }}"
|
||||
{% else %}
|
||||
href="/page/{{ pageParent._id }}"
|
||||
{% endif %}>
|
||||
{{ pageParent.title }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<time class="page__header-time">
|
||||
Last edit {{ (page.body.time / 1000) | date("M d Y") }}
|
||||
</time>
|
||||
|
@ -34,7 +37,9 @@
|
|||
{# Skip first header, because it is already showed as a Title #}
|
||||
{% if not (loop.first and block.type == 'header') %}
|
||||
{% if block.type in ['paragraph', 'header', 'image', 'code', 'list', 'delimiter', 'table', 'warning', 'checklist', 'linkTool', 'raw', 'embed'] %}
|
||||
{% include './blocks/' ~ block.type ~ '.twig' with block.data %}
|
||||
<div class="page__content-block">
|
||||
{% include './blocks/' ~ block.type ~ '.twig' with block.data %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
import app from '../backend/app';
|
||||
import app from '../backend/app.js';
|
||||
import http from 'http';
|
||||
import config from 'config';
|
||||
import Debug from 'debug';
|
||||
|
@ -96,4 +96,4 @@ function onListening(): void {
|
|||
export default {
|
||||
server,
|
||||
app,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { Storage } from '../utils/storage';
|
||||
import Shortcut from '@codexteam/shortcuts';
|
||||
|
||||
/**
|
||||
* Local storage key
|
||||
*/
|
||||
const LOCAL_STORAGE_KEY = 'docs_sidebar_state';
|
||||
|
||||
const SIDEBAR_VISIBILITY_KEY = 'docs_sidebar_visibility';
|
||||
|
||||
/**
|
||||
* Section list item height in px
|
||||
|
@ -31,6 +32,9 @@ export default class Sidebar {
|
|||
sectionList: 'docs-sidebar__section-list',
|
||||
sectionListItemActive: 'docs-sidebar__section-list-item--active',
|
||||
sidebarToggler: 'docs-sidebar__toggler',
|
||||
sidebarSlider: 'docs-sidebar__slider',
|
||||
sidebarCollapsed: 'docs-sidebar--collapsed',
|
||||
sidebarAnimated: 'docs-sidebar--animated',
|
||||
sidebarContent: 'docs-sidebar__content',
|
||||
sidebarContentHidden: 'docs-sidebar__content--hidden',
|
||||
sidebarContentInvisible: 'docs-sidebar__content--invisible',
|
||||
|
@ -45,14 +49,24 @@ export default class Sidebar {
|
|||
* Stores refs to HTML elements needed for correct sidebar work
|
||||
*/
|
||||
this.nodes = {
|
||||
sidebar: null,
|
||||
sections: [],
|
||||
sidebarContent: null,
|
||||
toggler: null,
|
||||
slider: null,
|
||||
};
|
||||
this.sidebarStorage = new Storage(LOCAL_STORAGE_KEY);
|
||||
const storedState = this.sidebarStorage.get();
|
||||
|
||||
this.sectionsState = storedState ? JSON.parse(storedState) : {};
|
||||
|
||||
// Initialize localStorage that contains sidebar visibility
|
||||
this.sidebarVisibilityStorage = new Storage(SIDEBAR_VISIBILITY_KEY);
|
||||
// Get current sidebar visibility from storage
|
||||
const storedVisibility = this.sidebarVisibilityStorage.get();
|
||||
|
||||
// Sidebar visibility
|
||||
this.isVisible = storedVisibility !== 'false';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -62,11 +76,14 @@ export default class Sidebar {
|
|||
* @param {HTMLElement} moduleEl - module element
|
||||
*/
|
||||
init(settings, moduleEl) {
|
||||
this.nodes.sidebar = moduleEl;
|
||||
this.nodes.sections = Array.from(moduleEl.querySelectorAll('.' + Sidebar.CSS.section));
|
||||
this.nodes.sections.forEach(section => this.initSection(section));
|
||||
this.nodes.sidebarContent = moduleEl.querySelector('.' + Sidebar.CSS.sidebarContent);
|
||||
this.nodes.toggler = moduleEl.querySelector('.' + Sidebar.CSS.sidebarToggler);
|
||||
this.nodes.toggler.addEventListener('click', () => this.toggleSidebar());
|
||||
this.nodes.slider = moduleEl.querySelector('.' + Sidebar.CSS.sidebarSlider);
|
||||
this.nodes.slider.addEventListener('click', () => this.handleSliderClick());
|
||||
this.ready();
|
||||
}
|
||||
|
||||
|
@ -104,7 +121,7 @@ export default class Sidebar {
|
|||
|
||||
const itemsCount = sectionList.children.length;
|
||||
|
||||
sectionList.style.maxHeight = `${ itemsCount * ITEM_HEIGHT }px`;
|
||||
sectionList.style.maxHeight = `${itemsCount * ITEM_HEIGHT}px`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -168,12 +185,52 @@ export default class Sidebar {
|
|||
this.nodes.sidebarContent.classList.toggle(Sidebar.CSS.sidebarContentHidden);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes sidebar
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
initSidebar() {
|
||||
if (!this.isVisible) {
|
||||
this.nodes.sidebar.classList.add(Sidebar.CSS.sidebarCollapsed);
|
||||
}
|
||||
|
||||
/**
|
||||
* prevent sidebar animation on page load
|
||||
* Since animated class contains transition, hiding will be animated with it
|
||||
* To prevent awkward animation when visibility is set to false, we need to remove animated class
|
||||
*/
|
||||
setTimeout(() => {
|
||||
this.nodes.sidebar.classList.add(Sidebar.CSS.sidebarAnimated);
|
||||
}, 200);
|
||||
|
||||
// add event listener to execute keyboard shortcut
|
||||
// eslint-disable-next-line no-new
|
||||
new Shortcut({
|
||||
name: 'CMD+.',
|
||||
on: document.body,
|
||||
callback: () => this.handleSliderClick(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Slides sidebar
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
handleSliderClick() {
|
||||
this.isVisible = !this.isVisible;
|
||||
this.sidebarVisibilityStorage.set(this.isVisible);
|
||||
this.nodes.sidebar.classList.toggle(Sidebar.CSS.sidebarCollapsed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays sidebar when ready
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
ready() {
|
||||
this.initSidebar();
|
||||
this.nodes.sidebarContent.classList.remove(Sidebar.CSS.sidebarContentInvisible);
|
||||
}
|
||||
}
|
||||
|
|
29
src/frontend/styles/components/error.pcss
Normal file
29
src/frontend/styles/components/error.pcss
Normal file
|
@ -0,0 +1,29 @@
|
|||
.error-page {
|
||||
font-size: 15px;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 45%;
|
||||
left: 50%;
|
||||
|
||||
@media (--mobile) {
|
||||
position: relative;
|
||||
top: 30vh;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
@media (--tablet) {
|
||||
position: relative;
|
||||
top: 30vh;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@media (--mobile) {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 40px 0 20px;
|
||||
}
|
||||
}
|
|
@ -10,15 +10,14 @@ html {
|
|||
border-bottom: 1px solid var(--color-line-gray);
|
||||
font-size: 18px;
|
||||
flex-wrap: wrap;
|
||||
height: var(--layout-height-header);
|
||||
box-sizing: border-box;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: white;
|
||||
z-index: 10;
|
||||
|
||||
@media (--mobile){
|
||||
line-height: 40px;
|
||||
@media (--not-mobile){
|
||||
height: var(--layout-height-header);
|
||||
}
|
||||
|
||||
&__menu-link,
|
||||
|
@ -36,11 +35,13 @@ html {
|
|||
}
|
||||
|
||||
&__menu-link {
|
||||
padding: 4px 10px;
|
||||
font-weight: 500;
|
||||
transition: background-color .13s;
|
||||
|
||||
@apply --squircle;
|
||||
@media (--not-mobile) {
|
||||
padding: 4px 10px;
|
||||
@apply --squircle;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-link-hover);
|
||||
|
@ -55,8 +56,9 @@ html {
|
|||
font-size: 16px;
|
||||
|
||||
@media (--mobile) {
|
||||
margin-top: 6px;
|
||||
flex-basis: 100%;
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
li {
|
||||
|
@ -85,8 +87,8 @@ html {
|
|||
@media (--mobile) {
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 15px;
|
||||
right: 0;
|
||||
top: 13px;
|
||||
line-height: 1em;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,13 +10,13 @@
|
|||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
background-color: var(--color-link-hover);
|
||||
border-radius: 10px;
|
||||
padding: 12px 16px 12px 16px;
|
||||
color: black;
|
||||
width: max-content;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
|
||||
@apply --squircle;
|
||||
|
||||
&--previous {
|
||||
align-items: flex-start;
|
||||
margin-left: 0;
|
||||
|
@ -29,13 +29,15 @@
|
|||
|
||||
&-direction {
|
||||
text-transform: capitalize;
|
||||
color: var(--color-direction-navigation);
|
||||
color: var(--color-text-second);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 140%;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
&-label {
|
||||
width: 100%;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,33 +1,38 @@
|
|||
.page {
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
letter-spacing: 0.005em;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
color: var(--color-text-second);
|
||||
line-height: 1.5em;
|
||||
|
||||
@media (--mobile) {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
&-nav {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@media (--mobile) {
|
||||
display: none;
|
||||
&-item {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
|
||||
@media (--mobile) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--color-link-active);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--color-link-active);
|
||||
}
|
||||
svg {
|
||||
margin: 0 6px;
|
||||
|
||||
&:not(:last-of-type) {
|
||||
&::after {
|
||||
content: '»';
|
||||
margin: 0 0.7em 0 0.45em;
|
||||
@media (--mobile) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,11 +52,7 @@
|
|||
}
|
||||
|
||||
&__title {
|
||||
@apply --font-serif;
|
||||
font-size: 35px;
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.04em;
|
||||
margin-bottom: -0.1em;
|
||||
@apply --text-content-title;
|
||||
}
|
||||
|
||||
.cdx-marker {
|
||||
|
@ -61,48 +62,20 @@
|
|||
|
||||
.inline-code,
|
||||
.block-header a .inline-code {
|
||||
display: inline-block;
|
||||
background: rgba(251,241,241,0.78);
|
||||
color: #C44545;
|
||||
padding: 0.1em 0.5em;
|
||||
border-radius: 2px;
|
||||
margin: 0 2px;
|
||||
font-family: Menlo, Monaco, Consolas, Courier New, monospace;
|
||||
font-size: 0.84em;
|
||||
line-height: 1.4em;
|
||||
border-bottom: 0;
|
||||
|
||||
&:hover {
|
||||
background: rgba(251,241,241,0.78);
|
||||
}
|
||||
@apply --text-inline-code;
|
||||
}
|
||||
|
||||
&__content {
|
||||
@apply --text-content-main;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid #000;
|
||||
padding-bottom: 1px;
|
||||
color: inherit;
|
||||
@apply --text-inline-link;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--color-link-active);
|
||||
border-bottom-color: var(--color-link-active);
|
||||
}
|
||||
|
||||
.inline-code {
|
||||
margin: 0;
|
||||
padding: 0.15em .5em;
|
||||
border-bottom: 1px dashed rgba(84, 151, 255, 0.99);
|
||||
color: #1f6fd8;
|
||||
background-color: #daf1fe;
|
||||
|
||||
&:hover {
|
||||
background-color: #c8edfe;
|
||||
}
|
||||
}
|
||||
&-block {
|
||||
@apply --content-block;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -110,6 +83,8 @@
|
|||
* ==================
|
||||
*/
|
||||
.block-paragraph {
|
||||
margin: 0;
|
||||
|
||||
.inline-code {
|
||||
-webkit-font-smoothing: initial;
|
||||
-moz-osx-font-smoothing: initial;
|
||||
|
@ -121,21 +96,14 @@
|
|||
* ==================
|
||||
*/
|
||||
.block-header {
|
||||
@apply --font-serif;
|
||||
margin: 2.1em 0 0.5em;
|
||||
@apply --text-header;
|
||||
|
||||
&--2 {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
@apply --text-header-2;
|
||||
}
|
||||
|
||||
&--3 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
& + p {
|
||||
margin-top: 0.5em;
|
||||
@apply --text-header-3;
|
||||
}
|
||||
|
||||
a {
|
||||
|
@ -185,14 +153,8 @@
|
|||
* ==================
|
||||
*/
|
||||
.block-code {
|
||||
background: var(--color-bg-light);
|
||||
border: 1px solid #f1f1f4;
|
||||
border-radius: 5px;
|
||||
font-family: var(--font-mono);
|
||||
line-height: 1.7em;
|
||||
font-size: 13px;
|
||||
overflow-x: auto;
|
||||
margin: 15px 0;
|
||||
@apply --text-code-block;
|
||||
@apply --squircle;
|
||||
|
||||
&__content {
|
||||
display: inline-block !important;
|
||||
|
@ -200,7 +162,7 @@
|
|||
word-wrap: normal;
|
||||
background: transparent !important;
|
||||
padding: 15px !important;
|
||||
color: #41314e !important;
|
||||
color: var(--color-code-main) !important;
|
||||
min-width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
|
@ -211,13 +173,31 @@
|
|||
}
|
||||
}
|
||||
|
||||
.hljs-params {
|
||||
color: var(--color-code-params);
|
||||
}
|
||||
|
||||
.hljs-number {
|
||||
color: var(--color-code-number);
|
||||
}
|
||||
|
||||
|
||||
.hljs-title,
|
||||
.hljs-title.class_,
|
||||
.hljs-title.class_.inherited__,
|
||||
.hljs-title.function_ {
|
||||
color: var(--color-code-class);
|
||||
}
|
||||
|
||||
|
||||
.hljs-name,
|
||||
.hljs-section{
|
||||
color: #359f3f;
|
||||
.hljs-section,
|
||||
.hljs-selector-tag {
|
||||
color: var(--color-code-tag);
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #718c77;
|
||||
color: var(--color-code-main);
|
||||
}
|
||||
|
||||
.hljs-attr,
|
||||
|
@ -226,7 +206,13 @@
|
|||
.hljs-selector-id,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-title {
|
||||
color: #904eb3;
|
||||
color: var(--color-code-class);
|
||||
}
|
||||
|
||||
.hljs-attribute,
|
||||
.hljs-literal,
|
||||
.hljs-operator {
|
||||
color: var(--color-code-variable);
|
||||
}
|
||||
|
||||
.hljs-emphasis,
|
||||
|
@ -235,8 +221,33 @@
|
|||
.hljs-strong,
|
||||
.hljs-template-variable,
|
||||
.hljs-variable {
|
||||
color: #c21f04;
|
||||
color: var(--color-code-string);
|
||||
}
|
||||
|
||||
.hljs-doctag,
|
||||
.hljs-keyword,
|
||||
.hljs-meta .hljs-keyword,
|
||||
.hljs-template-tag,
|
||||
.hljs-template-variable,
|
||||
.hljs-type {
|
||||
color: var(--color-code-keyword);
|
||||
}
|
||||
|
||||
.hljs-variable.language_ {
|
||||
color: var(--color-code-variable) ;
|
||||
}
|
||||
|
||||
.hljs-code,
|
||||
.hljs-comment,
|
||||
.hljs-formula {
|
||||
color: var(--color-code-comment);
|
||||
}
|
||||
|
||||
.hljs-regexp {
|
||||
color: var(--color-code-tag);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -244,10 +255,12 @@
|
|||
* ==================
|
||||
*/
|
||||
.block-list {
|
||||
margin: 20px 0;
|
||||
margin: 0;
|
||||
list-style: outside;
|
||||
padding-left: 26px;
|
||||
|
||||
li {
|
||||
margin: 10px 0;
|
||||
li:not(:last-of-type) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -323,7 +336,6 @@
|
|||
* ==================
|
||||
*/
|
||||
.block-table {
|
||||
margin: 20px 0;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
border-radius: 3px;
|
||||
|
@ -331,11 +343,12 @@
|
|||
border: 1px solid var(--color-line-gray);
|
||||
|
||||
td {
|
||||
padding: 8px 10px;
|
||||
padding: 6px 8px;
|
||||
border: 1px solid var(--color-line-gray);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Warning
|
||||
* ==================
|
||||
|
@ -343,10 +356,9 @@
|
|||
.block-warning {
|
||||
display: flex;
|
||||
padding: 20px;
|
||||
margin: 30px 0;
|
||||
border-radius: 7px;
|
||||
background: #fff9ef;
|
||||
color: #392e2f;
|
||||
background: #fffad0;
|
||||
|
||||
@apply --squircle;
|
||||
|
||||
&__icon {
|
||||
margin-right: 15px;
|
||||
|
|
|
@ -1,6 +1,34 @@
|
|||
.docs-sidebar {
|
||||
width: 100vw;
|
||||
|
||||
&--animated {
|
||||
.docs-sidebar__content {
|
||||
transition: transform 200ms ease-in-out;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.docs-sidebar__slider {
|
||||
transition: transform 200ms ease-in-out;
|
||||
will-change: transform;
|
||||
}
|
||||
}
|
||||
|
||||
&--collapsed {
|
||||
@media (--desktop) {
|
||||
.docs-sidebar__content {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
.docs-sidebar__slider {
|
||||
transform: translateX(20px);
|
||||
|
||||
svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (--desktop) {
|
||||
width: var(--layout-sidebar-width);
|
||||
}
|
||||
|
@ -22,6 +50,10 @@
|
|||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
@media (--mobile){
|
||||
margin: 0 -8px;
|
||||
}
|
||||
|
||||
&--hidden {
|
||||
display: none;
|
||||
}
|
||||
|
@ -78,6 +110,11 @@
|
|||
font-size: 14px;
|
||||
line-height: 21px;
|
||||
height: 29px;
|
||||
|
||||
@media (--mobile){
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
}
|
||||
}
|
||||
|
||||
&__section-title,
|
||||
|
@ -88,6 +125,7 @@
|
|||
padding: 0 8px;
|
||||
transition-property: background-color;
|
||||
transition-duration: 0.1s;
|
||||
|
||||
@apply --squircle;
|
||||
}
|
||||
|
||||
|
@ -173,8 +211,6 @@
|
|||
|
||||
|
||||
&__toggler {
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
color: var(--color-text-second);
|
||||
padding: 20px 15px;
|
||||
border-bottom: 1px solid var(--color-line-gray);
|
||||
|
@ -188,6 +224,24 @@
|
|||
}
|
||||
}
|
||||
|
||||
&__slider {
|
||||
display: none;
|
||||
position: fixed;
|
||||
transform: translateX(calc(var(--layout-sidebar-width) + 20px));
|
||||
bottom: 20px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
background-color: var(--color-link-hover);
|
||||
|
||||
@media (--desktop) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
&__logo {
|
||||
display: none;
|
||||
margin-top: auto;
|
||||
|
@ -196,6 +250,7 @@
|
|||
padding-bottom: 20px;
|
||||
padding-top: 60px;
|
||||
font-size: 14px;
|
||||
color: var(--color-text-second);
|
||||
|
||||
@media (--desktop) {
|
||||
display: block;
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
.table-of-content {
|
||||
border-left: 1px solid var(--color-line-gray);
|
||||
padding-left: var(--layout-padding-horizontal);
|
||||
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
overflow: auto;
|
||||
padding: var(--layout-padding-vertical) var(--layout-padding-horizontal);
|
||||
padding: 0 var(--layout-padding-horizontal);
|
||||
margin: var(--layout-padding-vertical) 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
--padding-x: 8px;
|
||||
|
||||
&__header {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
|
@ -14,7 +15,7 @@
|
|||
letter-spacing: -0.01em;
|
||||
|
||||
margin-bottom: 12px;
|
||||
padding: 0 6px;
|
||||
padding: 0 var(--padding-x);
|
||||
}
|
||||
|
||||
&__list {
|
||||
|
@ -27,7 +28,7 @@
|
|||
|
||||
list-style: none;
|
||||
|
||||
gap: 6px;
|
||||
gap: 2px;
|
||||
|
||||
&-item {
|
||||
@apply --squircle;
|
||||
|
@ -47,7 +48,7 @@
|
|||
&--indent-4x { margin-left: 24px; }
|
||||
|
||||
& > a {
|
||||
padding: 4px 8px;
|
||||
padding: 4px var(--padding-x);
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
letter-spacing: -0.01em;
|
||||
|
|
|
@ -52,25 +52,46 @@
|
|||
}
|
||||
|
||||
.writing-editor {
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
letter-spacing: 0.005em;
|
||||
@apply --text-content-main;
|
||||
|
||||
.ce-code__textarea {
|
||||
color: #41314e;
|
||||
line-height: 1.6em;
|
||||
font-size: 12px;
|
||||
background: var(--color-bg-light);
|
||||
border: 1px solid #f1f1f4;
|
||||
box-shadow: none;
|
||||
@apply --text-code-block;
|
||||
|
||||
border: 0;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.ce-paragraph a {
|
||||
color: inherit;
|
||||
@apply --text-inline-link;
|
||||
}
|
||||
|
||||
.ce-header {
|
||||
@apply --font-serif;
|
||||
@apply --text-header;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h2.ce-header {
|
||||
@apply --text-header-2;
|
||||
}
|
||||
|
||||
h3.ce-header {
|
||||
@apply --text-header-3;
|
||||
}
|
||||
|
||||
.cdx-block {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.inline-code {
|
||||
@apply --text-inline-code;
|
||||
}
|
||||
|
||||
.tc-table {
|
||||
@apply --text-content-main;
|
||||
}
|
||||
|
||||
.tc-cell {
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.ce-block__content {
|
||||
|
@ -79,5 +100,12 @@
|
|||
}
|
||||
|
||||
.codex-editor__redactor .ce-block:first-of-type .ce-header {
|
||||
font-size: 32px;
|
||||
@apply --text-content-title;
|
||||
}
|
||||
|
||||
|
||||
.ce-block {
|
||||
@apply --content-block;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,27 +2,23 @@
|
|||
display: inline-block;
|
||||
width: 100%;
|
||||
|
||||
span {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
&--added {
|
||||
color: #277030;
|
||||
background-color: #e2fce7;
|
||||
color: #70b979;
|
||||
background-color: #25f84d21;
|
||||
|
||||
&::before {
|
||||
content: '+';
|
||||
opacity: 0.4;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
&--removed {
|
||||
color: rgb(174, 54, 60);
|
||||
background-color: rgba(255, 230, 230, 1);
|
||||
color: #f1acaf;
|
||||
background-color: #95000069;
|
||||
|
||||
&::before {
|
||||
content: '-';
|
||||
opacity: 0.4;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
@import './components/page.pcss';
|
||||
@import './components/landing.pcss';
|
||||
@import './components/auth.pcss';
|
||||
@import './components/error.pcss';
|
||||
@import './components/button.pcss';
|
||||
@import './components/sidebar.pcss';
|
||||
@import './components/navigator.pcss';
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
:root {
|
||||
--color-text-main: #313649;
|
||||
--color-text-second: #5d6068;
|
||||
--color-direction-navigation: #717682;
|
||||
--color-text-main: #060C26;
|
||||
--color-text-second: #717682;
|
||||
--color-line-gray: #E8E8EB;
|
||||
--color-link-active: #2071cc;
|
||||
--color-link-hover: #F3F6F8;
|
||||
|
@ -10,6 +9,19 @@
|
|||
--color-input-primary: #F3F6F8;
|
||||
--color-input-border: #477CFF;
|
||||
|
||||
|
||||
/* Code Block styles */
|
||||
--color-code-bg: #252935;
|
||||
--color-code-main: #E1EBFE;
|
||||
--color-code-keyword: #ff6675;
|
||||
--color-code-class: #bf9dff;
|
||||
--color-code-variable: #69c6ff;
|
||||
--color-code-string: #81bcff;
|
||||
--color-code-params: #ffa259;
|
||||
--color-code-tag: #74e59d;
|
||||
--color-code-number: #ff6262;
|
||||
--color-code-comment: #6c7f93;
|
||||
|
||||
--color-button-primary: #3389FF;
|
||||
--color-button-primary-hover: #2E7AE6;
|
||||
--color-button-primary-active: #296DCC;
|
||||
|
@ -44,11 +56,6 @@
|
|||
|
||||
--font-mono: Menlo,Monaco,Consolas,Courier New,monospace;
|
||||
|
||||
--font-serif {
|
||||
font-family: "Lucida Grande","Lucida Sans Unicode","Lucida Sans", Geneva, Arial, sans-serif;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
--button {
|
||||
display: inline-block;
|
||||
padding: 9px 15px;
|
||||
|
@ -131,6 +138,120 @@
|
|||
-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 10.3872C0 1.83334 1.83334 0 10.3872 0H13.6128C22.1667 0 24 1.83334 24 10.3872V13.6128C24 22.1667 22.1667 24 13.6128 24H10.3872C1.83334 24 0 22.1667 0 13.6128V10.3872Z' fill='black'/%3E%3C/svg%3E%0A") 48% 41% 37.9% 53.3%;;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The common styles for the Page blocks as well as Editor blocks
|
||||
*/
|
||||
--content-block {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main page H1 title
|
||||
*/
|
||||
--text-content-title {
|
||||
font-size: 36px;
|
||||
font-weight: 800;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Page body font
|
||||
*/
|
||||
--text-content-main {
|
||||
font-size: 16px;
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Code on page and in the Editor
|
||||
*/
|
||||
--text-code-block {
|
||||
background: var(--color-code-bg);
|
||||
color: var(--color-code-main);
|
||||
font-family: var(--font-mono);
|
||||
line-height: 1.5em;
|
||||
font-size: 13px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common styles for text headings (H2, H3, H4)
|
||||
*/
|
||||
--text-header {
|
||||
margin: 18px 0 0;
|
||||
line-height: 1.35em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Styles for the H2 at the Page and Editor
|
||||
*/
|
||||
--text-header-2 {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/**
|
||||
* Styles for the H3 at the Page and Editor
|
||||
*/
|
||||
--text-header-3 {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/**
|
||||
* Styles for the inline code element at the Page and Editor
|
||||
*/
|
||||
--text-inline-code {
|
||||
display: inline-block;
|
||||
background: rgba(251,241,241,0.78);
|
||||
color: #C44545;
|
||||
font-size: 14px;
|
||||
line-height: 1.4em;
|
||||
letter-spacing: 0;
|
||||
|
||||
padding: 3px 4px 2px;
|
||||
margin: -1px 2px 0;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.84em;
|
||||
border-bottom: 0;
|
||||
|
||||
&:hover {
|
||||
background: rgba(251,241,241,0.78);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Styles for regular links inside a text
|
||||
*/
|
||||
--text-inline-link {
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid #000;
|
||||
padding-bottom: 1px;
|
||||
color: inherit;
|
||||
|
||||
&:hover {
|
||||
color: var(--color-link-active);
|
||||
border-bottom-color: var(--color-link-active);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a link contain an inline code, highlight it by its own way
|
||||
*/
|
||||
.inline-code {
|
||||
margin: 0;
|
||||
padding: 2px 5px;
|
||||
border-bottom: 1px dashed rgba(84, 151, 255, 0.99);
|
||||
color: #1f6fd8;
|
||||
background-color: #daf1fe;
|
||||
border-radius: 3px !important;
|
||||
|
||||
&:hover {
|
||||
background-color: #c8edfe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -140,5 +261,6 @@
|
|||
@custom-media --desktop all and (min-width: 1050px);
|
||||
@custom-media --tablet all and (min-width: 980px) and (max-width: 1050px);
|
||||
@custom-media --mobile all and (max-width: 980px);
|
||||
@custom-media --not-mobile all and (min-width: 981px);
|
||||
@custom-media --retina all and (-webkit-min-device-pixel-ratio: 1.5);
|
||||
@custom-media --can-hover all and (hover:hover);
|
||||
|
|
3
src/frontend/svg/arrow-left.svg
Normal file
3
src/frontend/svg/arrow-left.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="7" height="11" viewBox="0 0 7 11" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.95579 10.2458C6.14204 10.0584 6.24658 9.80498 6.24658 9.5408C6.24658 9.27661 6.14204 9.02316 5.95579 8.8358L2.41579 5.2458L5.95579 1.7058C6.14204 1.51844 6.24658 1.26498 6.24658 1.0008C6.24658 0.736612 6.14204 0.483161 5.95579 0.295798C5.86283 0.20207 5.75223 0.127675 5.63037 0.0769067C5.50851 0.026138 5.3778 0 5.24579 0C5.11378 0 4.98307 0.026138 4.86121 0.0769067C4.73935 0.127675 4.62875 0.20207 4.53579 0.295798L0.295789 4.5358C0.202061 4.62876 0.127667 4.73936 0.0768978 4.86122C0.0261292 4.98308 -9.32772e-06 5.11379 -9.32772e-06 5.2458C-9.32772e-06 5.37781 0.0261292 5.50852 0.0768978 5.63037C0.127667 5.75223 0.202061 5.86284 0.295789 5.9558L4.53579 10.2458C4.62875 10.3395 4.73935 10.4139 4.86121 10.4647C4.98307 10.5155 5.11378 10.5416 5.24579 10.5416C5.3778 10.5416 5.50851 10.5155 5.63037 10.4647C5.75223 10.4139 5.86283 10.3395 5.95579 10.2458Z" fill="#060C26"/>
|
||||
</svg>
|
After Width: | Height: | Size: 991 B |
1
src/frontend/svg/arrow-right.svg
Normal file
1
src/frontend/svg/arrow-right.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="21" height="20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8.037 6.08a.833.833 0 0 0 0 1.175l2.95 2.992-2.95 2.95a.833.833 0 1 0 1.183 1.175l3.533-3.534a.832.832 0 0 0 0-1.183L9.22 6.08a.833.833 0 0 0-1.183 0Z" fill="#717682"/></svg>
|
After Width: | Height: | Size: 260 B |
|
@ -3,7 +3,7 @@ import config from 'config';
|
|||
import { expect } from 'chai';
|
||||
import Datastore from 'nedb';
|
||||
|
||||
import { Database } from '../backend/utils/database';
|
||||
import { Database } from '../backend/utils/database/index.js';
|
||||
|
||||
interface Document {
|
||||
data?: any;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import chaiHTTP from 'chai-http';
|
||||
import chai, { expect } from 'chai';
|
||||
|
||||
import server from '../bin/server';
|
||||
import server from '../bin/server.js';
|
||||
|
||||
const app = server.app;
|
||||
|
||||
|
@ -16,4 +16,4 @@ describe('Express app', () => {
|
|||
|
||||
expect(result).to.have.status(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,12 +2,20 @@ import { expect } from 'chai';
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import config from 'config';
|
||||
import Alias from '../../backend/models/alias';
|
||||
import { binaryMD5 } from '../../backend/utils/crypto';
|
||||
import database from '../../backend/utils/database';
|
||||
import Alias from '../../backend/models/alias.js';
|
||||
import { binaryMD5 } from '../../backend/utils/crypto.js';
|
||||
import database from '../../backend/utils/database/index.js';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const aliases = database['aliases'];
|
||||
|
||||
/**
|
||||
* The __dirname CommonJS variables are not available in ES modules.
|
||||
* https://nodejs.org/api/esm.html#no-__filename-or-__dirname
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
describe('Alias model', () => {
|
||||
after(() => {
|
||||
const pathToDB = path.resolve(__dirname, '../../../', config.get('database'), './aliases.db');
|
||||
|
|
|
@ -2,8 +2,16 @@ import { expect } from 'chai';
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import config from 'config';
|
||||
import File from '../../backend/models/file';
|
||||
import database from '../../backend/utils/database';
|
||||
import File from '../../backend/models/file.js';
|
||||
import database from '../../backend/utils/database/index.js';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
/**
|
||||
* The __dirname CommonJS variables are not available in ES modules.
|
||||
* https://nodejs.org/api/esm.html#no-__filename-or-__dirname
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const files = database['files'];
|
||||
|
||||
|
@ -159,9 +167,9 @@ describe('File model', () => {
|
|||
|
||||
if (savedFile._id !== undefined){
|
||||
const foundFile = await File.get(savedFile._id);
|
||||
|
||||
|
||||
const { data } = foundFile;
|
||||
|
||||
|
||||
expect(data._id).to.equal(savedFile._id);
|
||||
expect(data.name).to.equal(savedFile.name);
|
||||
expect(data.filename).to.equal(savedFile.filename);
|
||||
|
@ -188,9 +196,9 @@ describe('File model', () => {
|
|||
|
||||
if (savedFile.filename !== undefined){
|
||||
const foundFile = await File.getByFilename(savedFile.filename);
|
||||
|
||||
|
||||
const { data } = foundFile;
|
||||
|
||||
|
||||
expect(data._id).to.equal(savedFile._id);
|
||||
expect(data.name).to.equal(savedFile.name);
|
||||
expect(data.filename).to.equal(savedFile.filename);
|
||||
|
|
|
@ -2,9 +2,17 @@ import { expect } from 'chai';
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import config from 'config';
|
||||
import Page from '../../backend/models/page';
|
||||
import translateString from '../../backend/utils/translation';
|
||||
import database from '../../backend/utils/database';
|
||||
import Page from '../../backend/models/page.js';
|
||||
import translateString from '../../backend/utils/translation.js';
|
||||
import database from '../../backend/utils/database/index.js';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
/**
|
||||
* The __dirname CommonJS variables are not available in ES modules.
|
||||
* https://nodejs.org/api/esm.html#no-__filename-or-__dirname
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const pages = database['pages'];
|
||||
|
||||
|
|
|
@ -2,8 +2,16 @@ import { expect } from 'chai';
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import config from 'config';
|
||||
import PageOrder from '../../backend/models/pageOrder';
|
||||
import database from '../../backend/utils/database';
|
||||
import PageOrder from '../../backend/models/pageOrder.js';
|
||||
import database from '../../backend/utils/database/index.js';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
/**
|
||||
* The __dirname CommonJS variables are not available in ES modules.
|
||||
* https://nodejs.org/api/esm.html#no-__filename-or-__dirname
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const pagesOrder = database['pagesOrder'];
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import path from 'path';
|
|||
import config from 'config';
|
||||
import sinon = require('sinon');
|
||||
|
||||
import rcParser from '../backend/utils/rcparser';
|
||||
import rcParser from '../backend/utils/rcparser.js';
|
||||
|
||||
const rcPath = path.resolve(process.cwd(), config.get('rcFile'));
|
||||
|
||||
|
|
|
@ -3,7 +3,15 @@ import path from 'path';
|
|||
import config from 'config';
|
||||
import chai from 'chai';
|
||||
import chaiHTTP from 'chai-http';
|
||||
import server from '../../bin/server';
|
||||
import server from '../../bin/server.js';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
/**
|
||||
* The __dirname CommonJS variables are not available in ES modules.
|
||||
* https://nodejs.org/api/esm.html#no-__filename-or-__dirname
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const {expect} = chai;
|
||||
const app = server.app;
|
||||
|
|
|
@ -3,11 +3,20 @@ import path from 'path';
|
|||
import config from 'config';
|
||||
import chai from 'chai';
|
||||
import chaiHTTP from 'chai-http';
|
||||
import server from '../../bin/server';
|
||||
import model from '../../backend/models/page';
|
||||
import Page from '../../backend/models/page';
|
||||
import PageOrder from '../../backend/models/pageOrder';
|
||||
import translateString from '../../backend/utils/translation';
|
||||
import server from '../../bin/server.js';
|
||||
import model from '../../backend/models/page.js';
|
||||
import Page from '../../backend/models/page.js';
|
||||
import PageOrder from '../../backend/models/pageOrder.js';
|
||||
import translateString from '../../backend/utils/translation.js';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
/**
|
||||
* The __dirname CommonJS variables are not available in ES modules.
|
||||
* https://nodejs.org/api/esm.html#no-__filename-or-__dirname
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
|
||||
const {expect} = chai;
|
||||
const app = server.app;
|
||||
|
@ -516,7 +525,7 @@ describe('Pages REST: ', () => {
|
|||
|
||||
page6 = await Page.get(pages[6]);
|
||||
expect(page6.data._id).to.be.undefined;
|
||||
|
||||
|
||||
page7 = await Page.get(pages[7]);
|
||||
expect(page7.data._id).to.be.undefined;
|
||||
|
||||
|
|
|
@ -5,8 +5,17 @@ import chai from 'chai';
|
|||
import chaiHTTP from 'chai-http';
|
||||
import rimraf from 'rimraf';
|
||||
import config from 'config';
|
||||
import server from '../../bin/server';
|
||||
import model from '../../backend/models/file';
|
||||
import server from '../../bin/server.js';
|
||||
import model from '../../backend/models/file.js';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
/**
|
||||
* The __dirname CommonJS variables are not available in ES modules.
|
||||
* https://nodejs.org/api/esm.html#no-__filename-or-__dirname
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
|
||||
const {expect} = chai;
|
||||
const app = server.app;
|
||||
|
@ -119,7 +128,7 @@ describe('Transport routes: ', () => {
|
|||
if (file.path !== undefined){
|
||||
const getRes = await agent
|
||||
.get(file.path);
|
||||
|
||||
|
||||
expect(getRes).to.have.status(200);
|
||||
expect(getRes).to.have.header('content-type', new RegExp(`^${file.mimetype}`));
|
||||
}
|
||||
|
@ -170,7 +179,7 @@ describe('Transport routes: ', () => {
|
|||
if (file.path !== undefined){
|
||||
const getRes = await agent
|
||||
.get(file.path);
|
||||
|
||||
|
||||
expect(getRes).to.have.status(200);
|
||||
expect(getRes).to.have.header('content-type', file.mimetype);
|
||||
}
|
||||
|
|
|
@ -4,9 +4,11 @@
|
|||
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "ES2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||
"target": "ES2021", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
|
||||
"module": "Node16", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
"lib": [ /* Specify library files to be included in the compilation. */
|
||||
"ESNext" /* ESNext includes new Level-4 features that were recently added to the ECMA-262 JS spec */
|
||||
],
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
|
||||
|
@ -44,13 +46,16 @@
|
|||
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
"moduleResolution": "NodeNext", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"types": [ /* Type declaration files to be included in compilation. */
|
||||
"node",
|
||||
"mocha"
|
||||
],
|
||||
"allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
|
@ -69,5 +74,12 @@
|
|||
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
"include": ["src/**/*"],
|
||||
"ts-node": {
|
||||
/**
|
||||
* Tell ts-node CLI to install the --loader automatically, explained below
|
||||
* https://typestrong.org/ts-node/docs/imports/
|
||||
*/
|
||||
"esm": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const path = require('path');
|
||||
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
/**
|
||||
* The __dirname CommonJS variables are not available in ES modules.
|
||||
* https://nodejs.org/api/esm.html#no-__filename-or-__dirname
|
||||
*/
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
/**
|
||||
* Options for the Babel
|
||||
|
@ -22,7 +29,7 @@ const babelLoader = {
|
|||
},
|
||||
};
|
||||
|
||||
module.exports = () => {
|
||||
export default () => {
|
||||
return {
|
||||
entry: './src/frontend/js/app.js',
|
||||
output: {
|
||||
|
@ -53,9 +60,18 @@ module.exports = () => {
|
|||
loader: 'postcss-loader',
|
||||
},
|
||||
],
|
||||
}, {
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /(node_modules|bower_components)/,
|
||||
resolve: {
|
||||
/**
|
||||
* Disable mandatory to specify full paths with extensions using '"type": "module"' in package.json
|
||||
* @see https://github.com/webpack/webpack/issues/11467#issuecomment-691873586
|
||||
* @see https://stackoverflow.com/questions/69427025/programmatic-webpack-jest-esm-cant-resolve-module-without-js-file-exten
|
||||
*/
|
||||
fullySpecified: false,
|
||||
},
|
||||
use: [
|
||||
babelLoader,
|
||||
],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue