mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-30 17:37:09 +02:00
refactor(v2): convert @docusaurus/plugin-content-blog
to TypeScript (#1785)
* convert `@docusaurus/plugin-content-blog` to typescript remove divided plugin convert `@docusaurus/plugin-content-blog` to typescript convert `@docusaurus/plugin-content-blog` to typescript convert `@docusaurus/plugin-content-blog` to typescript add `packages/docusaurus-plugin-content-blog/lib` to ignores linted refactoring type definition fix test fails lint * lint
This commit is contained in:
parent
0584407257
commit
78159f6dd5
13 changed files with 253 additions and 43 deletions
|
@ -13,4 +13,4 @@ packages/docusaurus-1.x/lib/core/__tests__/split-tab.test.js
|
||||||
packages/docusaurus-utils/lib/
|
packages/docusaurus-utils/lib/
|
||||||
packages/docusaurus/lib/
|
packages/docusaurus/lib/
|
||||||
packages/docusaurus-init/lib/
|
packages/docusaurus-init/lib/
|
||||||
|
packages/docusaurus-plugin-content-blog/lib/
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -15,5 +15,4 @@ types
|
||||||
packages/docusaurus-utils/lib/
|
packages/docusaurus-utils/lib/
|
||||||
packages/docusaurus/lib/
|
packages/docusaurus/lib/
|
||||||
packages/docusaurus-init/lib/
|
packages/docusaurus-init/lib/
|
||||||
|
packages/docusaurus-plugin-content-blog/lib/
|
||||||
|
|
||||||
|
|
|
@ -5,3 +5,4 @@ build
|
||||||
packages/docusaurus-utils/lib/
|
packages/docusaurus-utils/lib/
|
||||||
packages/docusaurus/lib/
|
packages/docusaurus/lib/
|
||||||
packages/docusaurus-init/lib/
|
packages/docusaurus-init/lib/
|
||||||
|
packages/docusaurus-plugin-content-blog/lib/
|
||||||
|
|
|
@ -17,6 +17,7 @@ module.exports = {
|
||||||
'__fixtures__',
|
'__fixtures__',
|
||||||
'/packages/docusaurus/lib',
|
'/packages/docusaurus/lib',
|
||||||
'/packages/docusaurus-utils/lib',
|
'/packages/docusaurus-utils/lib',
|
||||||
|
'/packages/docusaurus-plugin-content-blog/lib',
|
||||||
],
|
],
|
||||||
transform: {
|
transform: {
|
||||||
'^.+\\.[jt]sx?$': 'babel-jest',
|
'^.+\\.[jt]sx?$': 'babel-jest',
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
"name": "@docusaurus/plugin-content-blog",
|
"name": "@docusaurus/plugin-content-blog",
|
||||||
"version": "2.0.0-alpha.24",
|
"version": "2.0.0-alpha.24",
|
||||||
"description": "Blog plugin for Docusaurus",
|
"description": "Blog plugin for Docusaurus",
|
||||||
"main": "src/index.js",
|
"main": "lib/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"tsc": "tsc"
|
||||||
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,11 +8,12 @@
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import pluginContentBlog from '../index';
|
import pluginContentBlog from '../index';
|
||||||
|
import {DocusaurusConfig} from '../typesDocusaurus';
|
||||||
|
|
||||||
describe('loadBlog', () => {
|
describe('loadBlog', () => {
|
||||||
test('simple website', async () => {
|
test('simple website', async () => {
|
||||||
const siteDir = path.join(__dirname, '__fixtures__', 'website');
|
const siteDir = path.join(__dirname, '__fixtures__', 'website');
|
||||||
const siteConfig = {
|
const siteConfig: DocusaurusConfig = {
|
||||||
title: 'Hello',
|
title: 'Hello',
|
||||||
baseUrl: '/',
|
baseUrl: '/',
|
||||||
url: 'https://docusaurus.io',
|
url: 'https://docusaurus.io',
|
|
@ -4,25 +4,42 @@
|
||||||
* This source code is licensed under the MIT license found in the
|
* This source code is licensed under the MIT license found in the
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
import globby from 'globby';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import path from 'path';
|
||||||
|
import {parse, normalizeUrl, docuHash} from '@docusaurus/utils';
|
||||||
|
|
||||||
const fs = require('fs-extra');
|
import {
|
||||||
const globby = require('globby');
|
DateLink,
|
||||||
const _ = require('lodash');
|
PluginOptions,
|
||||||
const path = require('path');
|
BlogTags,
|
||||||
const {parse, normalizeUrl, docuHash} = require('@docusaurus/utils');
|
BlogPost,
|
||||||
|
Tag,
|
||||||
|
BlogContent,
|
||||||
|
BlogItemsToModules,
|
||||||
|
TagsModule,
|
||||||
|
ConfigureWebpackUtils,
|
||||||
|
} from './types';
|
||||||
|
import {
|
||||||
|
LoadContext,
|
||||||
|
PluginContentLoadedActions,
|
||||||
|
RouteModule,
|
||||||
|
} from './typesDocusaurus';
|
||||||
|
import {Configuration} from 'webpack';
|
||||||
|
|
||||||
// YYYY-MM-DD-{name}.mdx?
|
// YYYY-MM-DD-{name}.mdx?
|
||||||
// prefer named capture, but old node version do not support
|
// prefer named capture, but old node version do not support
|
||||||
const FILENAME_PATTERN = /^(\d{4}-\d{1,2}-\d{1,2})-?(.*?).mdx?$/;
|
const FILENAME_PATTERN = /^(\d{4}-\d{1,2}-\d{1,2})-?(.*?).mdx?$/;
|
||||||
|
|
||||||
function toUrl({date, link}) {
|
function toUrl({date, link}: DateLink) {
|
||||||
return `${date
|
return `${date
|
||||||
.toISOString()
|
.toISOString()
|
||||||
.substring(0, '2019-01-01'.length)
|
.substring(0, '2019-01-01'.length)
|
||||||
.replace(/-/g, '/')}/${link}`;
|
.replace(/-/g, '/')}/${link}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_OPTIONS = {
|
const DEFAULT_OPTIONS: PluginOptions = {
|
||||||
path: 'blog', // Path to data on filesystem, relative to site dir.
|
path: 'blog', // Path to data on filesystem, relative to site dir.
|
||||||
routeBasePath: 'blog', // URL Route.
|
routeBasePath: 'blog', // URL Route.
|
||||||
include: ['*.md', '*.mdx'], // Extensions to include.
|
include: ['*.md', '*.mdx'], // Extensions to include.
|
||||||
|
@ -36,7 +53,10 @@ const DEFAULT_OPTIONS = {
|
||||||
truncateMarker: /<!--\s*(truncate)\s*-->/, // string or regex
|
truncateMarker: /<!--\s*(truncate)\s*-->/, // string or regex
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = function(context, opts) {
|
export default function pluginContentBlog(
|
||||||
|
context: LoadContext,
|
||||||
|
opts: Partial<PluginOptions>,
|
||||||
|
) {
|
||||||
const options = {...DEFAULT_OPTIONS, ...opts};
|
const options = {...DEFAULT_OPTIONS, ...opts};
|
||||||
const contentPath = path.resolve(context.siteDir, options.path);
|
const contentPath = path.resolve(context.siteDir, options.path);
|
||||||
|
|
||||||
|
@ -64,10 +84,10 @@ module.exports = function(context, opts) {
|
||||||
cwd: blogDir,
|
cwd: blogDir,
|
||||||
});
|
});
|
||||||
|
|
||||||
const blogPosts = [];
|
const blogPosts: BlogPost[] = [];
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
blogFiles.map(async relativeSource => {
|
blogFiles.map(async (relativeSource: string) => {
|
||||||
// Cannot use path.join() as it resolves '../' and removes the '@site'. Let webpack loader resolve it.
|
// Cannot use path.join() as it resolves '../' and removes the '@site'. Let webpack loader resolve it.
|
||||||
const source = path.join(blogDir, relativeSource);
|
const source = path.join(blogDir, relativeSource);
|
||||||
const aliasedSource = `@site/${path.relative(siteDir, source)}`;
|
const aliasedSource = `@site/${path.relative(siteDir, source)}`;
|
||||||
|
@ -110,7 +130,9 @@ module.exports = function(context, opts) {
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
blogPosts.sort((a, b) => b.metadata.date - a.metadata.date);
|
blogPosts.sort(
|
||||||
|
(a, b) => b.metadata.date.getTime() - a.metadata.date.getTime(),
|
||||||
|
);
|
||||||
|
|
||||||
// Blog pagination routes.
|
// Blog pagination routes.
|
||||||
// Example: `/blog`, `/blog/page/1`, `/blog/page/2`
|
// Example: `/blog`, `/blog/page/1`, `/blog/page/2`
|
||||||
|
@ -120,7 +142,7 @@ module.exports = function(context, opts) {
|
||||||
|
|
||||||
const blogListPaginated = [];
|
const blogListPaginated = [];
|
||||||
|
|
||||||
function blogPaginationPermalink(page) {
|
function blogPaginationPermalink(page: number) {
|
||||||
return page > 0
|
return page > 0
|
||||||
? normalizeUrl([basePageUrl, `page/${page + 1}`])
|
? normalizeUrl([basePageUrl, `page/${page + 1}`])
|
||||||
: basePageUrl;
|
: basePageUrl;
|
||||||
|
@ -146,7 +168,7 @@ module.exports = function(context, opts) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const blogTags = {};
|
const blogTags: BlogTags = {};
|
||||||
const tagsPath = normalizeUrl([basePageUrl, 'tags']);
|
const tagsPath = normalizeUrl([basePageUrl, 'tags']);
|
||||||
blogPosts.forEach(blogPost => {
|
blogPosts.forEach(blogPost => {
|
||||||
const {tags} = blogPost.metadata;
|
const {tags} = blogPost.metadata;
|
||||||
|
@ -159,22 +181,26 @@ module.exports = function(context, opts) {
|
||||||
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
blogPost.metadata.tags = tags.map(tag => {
|
blogPost.metadata.tags = tags.map(tag => {
|
||||||
const normalizedTag = _.kebabCase(tag);
|
if (typeof tag === 'string') {
|
||||||
const permalink = normalizeUrl([tagsPath, normalizedTag]);
|
const normalizedTag = _.kebabCase(tag);
|
||||||
if (!blogTags[normalizedTag]) {
|
const permalink = normalizeUrl([tagsPath, normalizedTag]);
|
||||||
blogTags[normalizedTag] = {
|
if (!blogTags[normalizedTag]) {
|
||||||
name: tag.toLowerCase(), // Will only use the name of the first occurrence of the tag.
|
blogTags[normalizedTag] = {
|
||||||
items: [],
|
name: tag.toLowerCase(), // Will only use the name of the first occurrence of the tag.
|
||||||
|
items: [],
|
||||||
|
permalink,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
blogTags[normalizedTag].items.push(blogPost.id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: tag,
|
||||||
permalink,
|
permalink,
|
||||||
};
|
} as Tag;
|
||||||
|
} else {
|
||||||
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
blogTags[normalizedTag].items.push(blogPost.id);
|
|
||||||
|
|
||||||
return {
|
|
||||||
label: tag,
|
|
||||||
permalink,
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -189,7 +215,13 @@ module.exports = function(context, opts) {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
async contentLoaded({content: blogContents, actions}) {
|
async contentLoaded({
|
||||||
|
content: blogContents,
|
||||||
|
actions,
|
||||||
|
}: {
|
||||||
|
content: BlogContent;
|
||||||
|
actions: PluginContentLoadedActions;
|
||||||
|
}) {
|
||||||
if (!blogContents) {
|
if (!blogContents) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -209,7 +241,7 @@ module.exports = function(context, opts) {
|
||||||
blogTagsListPath,
|
blogTagsListPath,
|
||||||
} = blogContents;
|
} = blogContents;
|
||||||
|
|
||||||
const blogItemsToModules = {};
|
const blogItemsToModules: BlogItemsToModules = {};
|
||||||
// Create routes for blog entries.
|
// Create routes for blog entries.
|
||||||
const blogItems = await Promise.all(
|
const blogItems = await Promise.all(
|
||||||
blogPosts.map(async blogPost => {
|
blogPosts.map(async blogPost => {
|
||||||
|
@ -245,7 +277,7 @@ module.exports = function(context, opts) {
|
||||||
metadata: metadataPath,
|
metadata: metadataPath,
|
||||||
prevItem: prevItem && prevItem.metadataPath,
|
prevItem: prevItem && prevItem.metadataPath,
|
||||||
nextItem: nextItem && nextItem.metadataPath,
|
nextItem: nextItem && nextItem.metadataPath,
|
||||||
},
|
} as RouteModule,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -288,7 +320,7 @@ module.exports = function(context, opts) {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Tags.
|
// Tags.
|
||||||
const tagsModule = {};
|
const tagsModule: TagsModule = {};
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Object.keys(blogTags).map(async tag => {
|
Object.keys(blogTags).map(async tag => {
|
||||||
|
@ -352,7 +384,11 @@ module.exports = function(context, opts) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
configureWebpack(config, isServer, {getBabelLoader, getCacheLoader}) {
|
configureWebpack(
|
||||||
|
_config: Configuration,
|
||||||
|
isServer: boolean,
|
||||||
|
{getBabelLoader, getCacheLoader}: ConfigureWebpackUtils,
|
||||||
|
) {
|
||||||
const {rehypePlugins, remarkPlugins, truncateMarker} = options;
|
const {rehypePlugins, remarkPlugins, truncateMarker} = options;
|
||||||
return {
|
return {
|
||||||
module: {
|
module: {
|
||||||
|
@ -383,4 +419,4 @@ module.exports = function(context, opts) {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
}
|
|
@ -6,8 +6,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const {parseQuery, getOptions} = require('loader-utils');
|
const {parseQuery, getOptions} = require('loader-utils');
|
||||||
|
import {loader} from 'webpack';
|
||||||
|
|
||||||
module.exports = async function(fileString) {
|
export = function(fileString: string) {
|
||||||
const callback = this.async();
|
const callback = this.async();
|
||||||
|
|
||||||
const {truncateMarker} = getOptions(this);
|
const {truncateMarker} = getOptions(this);
|
||||||
|
@ -25,5 +26,5 @@ module.exports = async function(fileString) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
finalContent = fileString.split(truncateMarker)[0];
|
finalContent = fileString.split(truncateMarker)[0];
|
||||||
}
|
}
|
||||||
return callback(null, finalContent);
|
return callback && callback(null, finalContent);
|
||||||
};
|
} as loader.Loader;
|
93
packages/docusaurus-plugin-content-blog/src/types.ts
Normal file
93
packages/docusaurus-plugin-content-blog/src/types.ts
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
import {Loader} from 'webpack';
|
||||||
|
|
||||||
|
export interface BlogContent {
|
||||||
|
blogPosts: BlogPost[];
|
||||||
|
blogListPaginated: BlogPaginated[];
|
||||||
|
blogTags: BlogTags;
|
||||||
|
blogTagsListPath: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DateLink {
|
||||||
|
date: Date;
|
||||||
|
link: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PluginOptions {
|
||||||
|
path: string;
|
||||||
|
routeBasePath: string;
|
||||||
|
include: string[];
|
||||||
|
postsPerPage: number;
|
||||||
|
blogListComponent: string;
|
||||||
|
blogPostComponent: string;
|
||||||
|
blogTagsListComponent: string;
|
||||||
|
blogTagsPostsComponent: string;
|
||||||
|
remarkPlugins: string[];
|
||||||
|
rehypePlugins: string[];
|
||||||
|
truncateMarker: RegExp | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BlogTags {
|
||||||
|
[key: string]: BlogTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BlogTag {
|
||||||
|
name: string;
|
||||||
|
items: string[];
|
||||||
|
permalink: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BlogPost {
|
||||||
|
id: string;
|
||||||
|
metadata: MetaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BlogPaginated {
|
||||||
|
metadata: MetaData;
|
||||||
|
items: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MetaData {
|
||||||
|
permalink: string;
|
||||||
|
source: string;
|
||||||
|
description: string;
|
||||||
|
date: Date;
|
||||||
|
tags: (Tag | string)[];
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Tag {
|
||||||
|
label: string;
|
||||||
|
permalink: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BlogItemsToModules {
|
||||||
|
[key: string]: MetaDataWithPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MetaDataWithPath {
|
||||||
|
metadata: MetaData;
|
||||||
|
metadataPath: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TagsModule {
|
||||||
|
[key: string]: TagModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TagModule {
|
||||||
|
allTagsPath: string;
|
||||||
|
slug: string;
|
||||||
|
name: string;
|
||||||
|
count: number;
|
||||||
|
permalink: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConfigureWebpackUtils {
|
||||||
|
getStyleLoaders: (
|
||||||
|
isServer: boolean,
|
||||||
|
cssOptions: {
|
||||||
|
[key: string]: any;
|
||||||
|
},
|
||||||
|
) => Loader[];
|
||||||
|
getCacheLoader: (isServer: boolean, cacheOptions?: {}) => Loader | null;
|
||||||
|
getBabelLoader: (isServer: boolean, babelOptions?: {}) => Loader;
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
import {ParsedUrlQueryInput} from 'querystring';
|
||||||
|
|
||||||
|
export interface DocusaurusConfig {
|
||||||
|
baseUrl: string;
|
||||||
|
favicon?: string;
|
||||||
|
tagline?: string;
|
||||||
|
title: string;
|
||||||
|
url: string;
|
||||||
|
organizationName?: string;
|
||||||
|
projectName?: string;
|
||||||
|
githubHost?: string;
|
||||||
|
plugins?: PluginConfig[];
|
||||||
|
themes?: PluginConfig[];
|
||||||
|
presets?: PresetConfig[];
|
||||||
|
themeConfig?: {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
customFields?: {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PluginConfig = [string, Object | undefined] | string;
|
||||||
|
|
||||||
|
export type PresetConfig = [string, Object | undefined] | string;
|
||||||
|
|
||||||
|
export interface CLIOptions {
|
||||||
|
[option: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LoadContext {
|
||||||
|
siteDir: string;
|
||||||
|
generatedFilesDir?: string;
|
||||||
|
siteConfig: DocusaurusConfig;
|
||||||
|
cliOptions?: CLIOptions;
|
||||||
|
outDir?: string;
|
||||||
|
baseUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PluginContentLoadedActions {
|
||||||
|
addRoute(config: RouteConfig): void;
|
||||||
|
createData(name: string, data: Object): Promise<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Module =
|
||||||
|
| {
|
||||||
|
path: string;
|
||||||
|
__import?: boolean;
|
||||||
|
query?: ParsedUrlQueryInput;
|
||||||
|
}
|
||||||
|
| string;
|
||||||
|
|
||||||
|
export interface RouteModule {
|
||||||
|
[module: string]: Module | RouteModule | RouteModule[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RouteConfig {
|
||||||
|
path: string;
|
||||||
|
component: string;
|
||||||
|
modules?: RouteModule;
|
||||||
|
routes?: RouteConfig[];
|
||||||
|
exact?: boolean;
|
||||||
|
}
|
9
packages/docusaurus-plugin-content-blog/tsconfig.json
Normal file
9
packages/docusaurus-plugin-content-blog/tsconfig.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"incremental": true,
|
||||||
|
"tsBuildInfoFile": "./lib/.tsbuildinfo",
|
||||||
|
"rootDir": "src",
|
||||||
|
"outDir": "lib",
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,7 +50,7 @@ export async function loadPlugins({
|
||||||
|
|
||||||
// module is any valid module identifier - npm package or locally-resolved path.
|
// module is any valid module identifier - npm package or locally-resolved path.
|
||||||
const pluginModule = importFresh(pluginModuleImport);
|
const pluginModule = importFresh(pluginModuleImport);
|
||||||
return pluginModule(context, pluginOptions);
|
return (pluginModule.default || pluginModule)(context, pluginOptions);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,10 @@ export function loadPresets(
|
||||||
}
|
}
|
||||||
|
|
||||||
const presetModule = importFresh(presetModuleImport);
|
const presetModule = importFresh(presetModuleImport);
|
||||||
const preset: Preset = presetModule(context, presetOptions);
|
const preset: Preset = (presetModule.default || presetModule)(
|
||||||
|
context,
|
||||||
|
presetOptions,
|
||||||
|
);
|
||||||
|
|
||||||
preset.plugins && unflatPlugins.push(preset.plugins);
|
preset.plugins && unflatPlugins.push(preset.plugins);
|
||||||
preset.themes && unflatThemes.push(preset.themes);
|
preset.themes && unflatThemes.push(preset.themes);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue