Feature/website overhaul (#49)
* Reorganize repo * Overhaul website design and rewrite in NextJS and Typescript * Delete website-publish.yml
This commit is contained in:
parent
3674ee5bd8
commit
421ecf17e6
32
.github/workflows/website-publish.yml
vendored
32
.github/workflows/website-publish.yml
vendored
@ -1,32 +0,0 @@
|
||||
name: Website Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- website/**
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: 'website/yarn.lock'
|
||||
- name: Install
|
||||
working-directory: website
|
||||
run: yarn install --immutable
|
||||
- name: Build
|
||||
working-directory: website
|
||||
run: yarn build
|
||||
- name: Deploy
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.ACCESS_TOKEN }}
|
||||
publish_dir: website/dist
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,6 +1,5 @@
|
||||
dist/
|
||||
bin/
|
||||
public/
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
.DS_Store
|
||||
@ -20,3 +19,4 @@ coverage/
|
||||
.env
|
||||
.temp/
|
||||
*.tgz
|
||||
old-website
|
||||
|
33
.stylelintrc
33
.stylelintrc
@ -1,33 +0,0 @@
|
||||
{
|
||||
"processors": ["stylelint-processor-styled-components"],
|
||||
"extends": ["stylelint-config-standard-scss", "stylelint-config-styled-components"],
|
||||
"customSyntax": "postcss-scss",
|
||||
"rules": {
|
||||
"block-no-empty": null,
|
||||
"no-duplicate-selectors": null,
|
||||
"no-empty-source": null,
|
||||
"no-extra-semicolons": null,
|
||||
"declaration-empty-line-before": null,
|
||||
"string-quotes": null,
|
||||
"selector-class-pattern": null,
|
||||
"selector-pseudo-element-colon-notation": null,
|
||||
"rule-empty-line-before": null,
|
||||
"declaration-colon-newline-after": null,
|
||||
"at-rule-empty-line-before": null,
|
||||
"alpha-value-notation": null,
|
||||
"color-function-notation": null,
|
||||
"keyframes-name-pattern": null,
|
||||
"value-list-comma-newline-after": null,
|
||||
"no-descending-specificity": null,
|
||||
"selector-type-no-unknown": [
|
||||
true,
|
||||
{
|
||||
"ignoreTypes": ["$dummyValue"]
|
||||
}
|
||||
],
|
||||
"value-keyword-case": [
|
||||
"lower",
|
||||
{ "ignoreKeywords": ["dummyValue"], "camelCaseSvgKeywords": true }
|
||||
]
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
StylesPath = website/src/writing-guide/styles
|
||||
Vocab = website/src/writing-guide/vocab
|
||||
MinAlertLevel = warning
|
||||
|
||||
[*.md]
|
||||
BasedOnStyles = Avoid, Replace
|
87
README.md
87
README.md
@ -3,6 +3,7 @@
|
||||
|
||||
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/StaticJsCMS/static-cms/blob/main/LICENSE)
|
||||
[![npm latest package](https://img.shields.io/npm/v/@staticcms/core/latest.svg)](https://www.npmjs.com/package/@staticcms/core)
|
||||
[![npm next package](https://img.shields.io/npm/v/@staticcms/core/next.svg)](https://www.npmjs.com/package/@staticcms/core/v/next)
|
||||
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/StaticJsCMS/static-cms/blob/main/CONTRIBUTING.md)
|
||||
|
||||
</div>
|
||||
@ -13,6 +14,8 @@ A CMS for static site generators. Give users a simple way to edit
|
||||
and add content to any site built with a static site generator.
|
||||
|
||||
> **DISCLAIMER**: This package/repository is under heavy development and as such is very unstable at current. A stable release is targeted for the end of Novemeber and will be labeled `1.0.0`. Documentation may not be completely accurate prior to the `1.0.0` release.
|
||||
>
|
||||
> An alpha build for the stable release is available under the `@next` tack on npm.
|
||||
|
||||
## Community Chat
|
||||
|
||||
@ -35,87 +38,9 @@ Read more about Static CMS [Core Concepts](https://staticjscms.github.io/static-
|
||||
The Static CMS can be used in two different ways.
|
||||
|
||||
* A Quick and easy install, that requires you to create a single HTML file and a configuration file. All the CMS JavaScript and CSS are loaded from a CDN.
|
||||
To learn more about this installation method, refer to the [Quick Start Guide](https://staticjscms.github.io/static-cms/docs/start-with-a-template/)
|
||||
To learn more about this installation method, refer to the [CDN Hosting Guide](https://staticjscms.github.io/static-cms/docs/add-to-your-site-cdn/)
|
||||
* A complete, more complex install, that gives you more flexibility but requires that you use a static site builder with a build system that supports npm packages.
|
||||
|
||||
# static-cms-core
|
||||
|
||||
## Installation
|
||||
|
||||
`npm install @staticcms/core`
|
||||
|
||||
## Setup
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
import {
|
||||
AzureBackend,
|
||||
BitbucketBackend,
|
||||
BooleanWidget,
|
||||
CodeWidget,
|
||||
ColorStringWidget,
|
||||
DateTimeWidget,
|
||||
FileWidget,
|
||||
GitGatewayBackend,
|
||||
GitHubBackend,
|
||||
GitLabBackend,
|
||||
imageEditorComponent,
|
||||
ImageWidget,
|
||||
ListWidget,
|
||||
MapWidget,
|
||||
MarkdownWidget,
|
||||
StaticCmsCore as CMS,
|
||||
NumberWidget,
|
||||
ObjectWidget,
|
||||
ProxyBackend,
|
||||
RelationWidget,
|
||||
SelectWidget,
|
||||
StringWidget,
|
||||
TestBackend,
|
||||
TextWidget,
|
||||
locales,
|
||||
Icon,
|
||||
images
|
||||
} from '@static-cms/static-cms-core';
|
||||
|
||||
// Register all the things
|
||||
CMS.registerBackend('git-gateway', GitGatewayBackend);
|
||||
CMS.registerBackend('azure', AzureBackend);
|
||||
CMS.registerBackend('github', GitHubBackend);
|
||||
CMS.registerBackend('gitlab', GitLabBackend);
|
||||
CMS.registerBackend('bitbucket', BitbucketBackend);
|
||||
CMS.registerBackend('test-repo', TestBackend);
|
||||
CMS.registerBackend('proxy', ProxyBackend);
|
||||
CMS.registerWidget([
|
||||
StringWidget.Widget(),
|
||||
NumberWidget.Widget(),
|
||||
TextWidget.Widget(),
|
||||
ImageWidget.Widget(),
|
||||
FileWidget.Widget(),
|
||||
SelectWidget.Widget(),
|
||||
MarkdownWidget.Widget(),
|
||||
ListWidget.Widget(),
|
||||
ObjectWidget.Widget(),
|
||||
RelationWidget.Widget(),
|
||||
BooleanWidget.Widget(),
|
||||
MapWidget.Widget(),
|
||||
DateTimeWidget.Widget(),
|
||||
CodeWidget.Widget(),
|
||||
ColorStringWidget.Widget(),
|
||||
]);
|
||||
CMS.registerEditorComponent(imageEditorComponent);
|
||||
CMS.registerEditorComponent({
|
||||
id: 'code-block',
|
||||
label: 'Code Block',
|
||||
widget: 'code',
|
||||
type: 'code-block',
|
||||
});
|
||||
CMS.registerLocale('en', locales.en);
|
||||
|
||||
Object.keys(images).forEach(iconName => {
|
||||
CMS.registerIcon(iconName, <Icon type={iconName} />);
|
||||
});
|
||||
```
|
||||
To learn more about this installation method, refer to the [Bundling Guide](https://staticjscms.github.io/static-cms/docs/add-to-your-site-bundling/)
|
||||
|
||||
# Contributing
|
||||
|
||||
@ -133,6 +58,6 @@ Please make sure you understand its [implications and guarantees](https://writin
|
||||
|
||||
# Netlify CMS
|
||||
|
||||
Static CMS is a fork of Netlify CMS focusing on the core product over adding massive new features.
|
||||
Static CMS is a fork of Netlify CMS focusing on the core product over adding massive, scope expanding, new features.
|
||||
|
||||
# Thanks
|
||||
|
Before Width: | Height: | Size: 808 KiB After Width: | Height: | Size: 808 KiB |
Before Width: | Height: | Size: 310 KiB After Width: | Height: | Size: 310 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
@ -52,17 +52,13 @@
|
||||
"@emotion/styled": "11.10.4",
|
||||
"@iarna/toml": "2.2.5",
|
||||
"@mui/icons-material": "5.10.6",
|
||||
"@mui/material": "5.10.6",
|
||||
"@mui/material": "5.10.10",
|
||||
"@mui/x-date-pickers": "5.0.4",
|
||||
"@reduxjs/toolkit": "1.8.5",
|
||||
"@toast-ui/react-editor": "3.2.2",
|
||||
"ajv": "8.11.0",
|
||||
"ajv-errors": "3.0.0",
|
||||
"ajv-keywords": "5.1.0",
|
||||
"apollo-cache-inmemory": "1.6.6",
|
||||
"apollo-client": "2.6.10",
|
||||
"apollo-link-context": "1.0.20",
|
||||
"apollo-link-http": "1.5.17",
|
||||
"array-move": "4.0.0",
|
||||
"buffer": "6.0.3",
|
||||
"clean-stack": "4.2.0",
|
@ -19,7 +19,6 @@ import {
|
||||
} from '../../lib/util';
|
||||
import API, { API_NAME } from './API';
|
||||
import AuthenticationPage from './AuthenticationPage';
|
||||
import GraphQLAPI from './GraphQLAPI';
|
||||
|
||||
import type { Octokit } from '@octokit/rest';
|
||||
import type { Semaphore } from 'semaphore';
|
||||
@ -66,7 +65,6 @@ export default class GitHub implements BackendClass {
|
||||
apiRoot: string;
|
||||
mediaFolder?: string;
|
||||
token: string | null;
|
||||
useGraphql: boolean;
|
||||
_currentUserPromise?: Promise<GitHubUser>;
|
||||
_userIsOriginMaintainerPromises?: {
|
||||
[key: string]: Promise<boolean>;
|
||||
@ -92,7 +90,6 @@ export default class GitHub implements BackendClass {
|
||||
this.branch = config.backend.branch?.trim() || 'main';
|
||||
this.apiRoot = config.backend.api_root || 'https://api.github.com';
|
||||
this.token = '';
|
||||
this.useGraphql = config.backend.use_graphql || false;
|
||||
this.mediaFolder = config.media_folder;
|
||||
this.lock = asyncLock();
|
||||
}
|
||||
@ -179,7 +176,7 @@ export default class GitHub implements BackendClass {
|
||||
|
||||
async authenticate(state: Credentials) {
|
||||
this.token = state.token as string;
|
||||
const apiCtor = this.useGraphql ? GraphQLAPI : API;
|
||||
const apiCtor = API;
|
||||
this.api = new apiCtor({
|
||||
token: this.token,
|
||||
branch: this.branch,
|
@ -1,7 +1,3 @@
|
||||
import { InMemoryCache } from 'apollo-cache-inmemory';
|
||||
import { ApolloClient } from 'apollo-client';
|
||||
import { setContext } from 'apollo-link-context';
|
||||
import { createHttpLink } from 'apollo-link-http';
|
||||
import { Base64 } from 'js-base64';
|
||||
import partial from 'lodash/partial';
|
||||
import result from 'lodash/result';
|
||||
@ -20,25 +16,18 @@ import {
|
||||
throwOnConflictingBranches,
|
||||
unsentRequest,
|
||||
} from '../../lib/util';
|
||||
import * as queries from './queries';
|
||||
|
||||
import type { NormalizedCacheObject } from 'apollo-cache-inmemory';
|
||||
import type { ApolloQueryResult } from 'apollo-client';
|
||||
import type { DataFile, ImplementationFile, PersistOptions } from '../../interface';
|
||||
import type { DataFile, PersistOptions } from '../../interface';
|
||||
import type { ApiRequest, FetchError } from '../../lib/util';
|
||||
import type AssetProxy from '../../valueObjects/AssetProxy';
|
||||
|
||||
export const API_NAME = 'GitLab';
|
||||
|
||||
const NO_CACHE = 'no-cache';
|
||||
|
||||
export interface Config {
|
||||
apiRoot?: string;
|
||||
graphQLAPIRoot?: string;
|
||||
token?: string;
|
||||
branch?: string;
|
||||
repo?: string;
|
||||
useGraphQL?: boolean;
|
||||
}
|
||||
|
||||
export interface CommitAuthor {
|
||||
@ -130,65 +119,20 @@ export function getMaxAccess(groups: { group_access_level: number }[]) {
|
||||
}, groups[0]);
|
||||
}
|
||||
|
||||
function batch<T>(items: T[], maxPerBatch: number, action: (items: T[]) => void) {
|
||||
for (let index = 0; index < items.length; index = index + maxPerBatch) {
|
||||
const itemsSlice = items.slice(index, index + maxPerBatch);
|
||||
action(itemsSlice);
|
||||
}
|
||||
}
|
||||
|
||||
export default class API {
|
||||
apiRoot: string;
|
||||
graphQLAPIRoot: string;
|
||||
token: string | boolean;
|
||||
branch: string;
|
||||
repo: string;
|
||||
repoURL: string;
|
||||
commitAuthor?: CommitAuthor;
|
||||
|
||||
graphQLClient?: ApolloClient<NormalizedCacheObject>;
|
||||
|
||||
constructor(config: Config) {
|
||||
this.apiRoot = config.apiRoot || 'https://gitlab.com/api/v4';
|
||||
this.graphQLAPIRoot = config.graphQLAPIRoot || 'https://gitlab.com/api/graphql';
|
||||
this.token = config.token || false;
|
||||
this.branch = config.branch || 'main';
|
||||
this.repo = config.repo || '';
|
||||
this.repoURL = `/projects/${encodeURIComponent(this.repo)}`;
|
||||
if (config.useGraphQL === true) {
|
||||
this.graphQLClient = this.getApolloClient();
|
||||
}
|
||||
}
|
||||
|
||||
getApolloClient() {
|
||||
const authLink = setContext((_, { headers }) => {
|
||||
return {
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
...headers,
|
||||
authorization: this.token ? `token ${this.token}` : '',
|
||||
},
|
||||
};
|
||||
});
|
||||
const httpLink = createHttpLink({ uri: this.graphQLAPIRoot });
|
||||
return new ApolloClient({
|
||||
link: authLink.concat(httpLink),
|
||||
cache: new InMemoryCache(),
|
||||
defaultOptions: {
|
||||
watchQuery: {
|
||||
fetchPolicy: NO_CACHE,
|
||||
errorPolicy: 'ignore',
|
||||
},
|
||||
query: {
|
||||
fetchPolicy: NO_CACHE,
|
||||
errorPolicy: 'all',
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
reset() {
|
||||
return this.graphQLClient?.resetStore();
|
||||
}
|
||||
|
||||
withAuthorizationHeaders = (req: ApiRequest) => {
|
||||
@ -374,102 +318,7 @@ export default class API {
|
||||
};
|
||||
};
|
||||
|
||||
listAllFilesGraphQL = async (path: string, recursive: boolean, branch: String) => {
|
||||
const files: FileEntry[] = [];
|
||||
let blobsPaths;
|
||||
let cursor;
|
||||
do {
|
||||
blobsPaths = await this.graphQLClient!.query({
|
||||
query: queries.files,
|
||||
variables: { repo: this.repo, branch, path, recursive, cursor },
|
||||
});
|
||||
files.push(...blobsPaths.data.project.repository.tree.blobs.nodes);
|
||||
cursor = blobsPaths.data.project.repository.tree.blobs.pageInfo.endCursor;
|
||||
} while (blobsPaths.data.project.repository.tree.blobs.pageInfo.hasNextPage);
|
||||
|
||||
return files;
|
||||
};
|
||||
|
||||
readFilesGraphQL = async (files: ImplementationFile[]) => {
|
||||
const paths = files.map(({ path }) => path);
|
||||
|
||||
type BlobResult = {
|
||||
project: { repository: { blobs: { nodes: { id: string; data: string }[] } } };
|
||||
};
|
||||
|
||||
const blobPromises: Promise<ApolloQueryResult<BlobResult>>[] = [];
|
||||
batch(paths, 90, slice => {
|
||||
blobPromises.push(
|
||||
this.graphQLClient!.query({
|
||||
query: queries.blobs,
|
||||
variables: {
|
||||
repo: this.repo,
|
||||
branch: this.branch,
|
||||
paths: slice,
|
||||
},
|
||||
fetchPolicy: 'cache-first',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
type LastCommit = {
|
||||
id: string;
|
||||
authoredDate: string;
|
||||
authorName: string;
|
||||
author?: {
|
||||
name: string;
|
||||
username: string;
|
||||
publicEmail: string;
|
||||
};
|
||||
};
|
||||
|
||||
type CommitResult = {
|
||||
project: { repository: { [tree: string]: { lastCommit: LastCommit } } };
|
||||
};
|
||||
|
||||
const commitPromises: Promise<ApolloQueryResult<CommitResult>>[] = [];
|
||||
batch(paths, 8, slice => {
|
||||
commitPromises.push(
|
||||
this.graphQLClient!.query({
|
||||
query: queries.lastCommits(slice),
|
||||
variables: {
|
||||
repo: this.repo,
|
||||
branch: this.branch,
|
||||
},
|
||||
fetchPolicy: 'cache-first',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
const [blobsResults, commitsResults] = await Promise.all([
|
||||
(await Promise.all(blobPromises)).map(result => result.data.project.repository.blobs.nodes),
|
||||
(
|
||||
await Promise.all(commitPromises)
|
||||
).map(
|
||||
result =>
|
||||
Object.values(result.data.project.repository)
|
||||
.map(({ lastCommit }) => lastCommit)
|
||||
.filter(Boolean) as LastCommit[],
|
||||
),
|
||||
]);
|
||||
|
||||
const blobs = blobsResults.flat().map(result => result.data) as string[];
|
||||
const metadata = commitsResults.flat().map(({ author, authoredDate, authorName }) => ({
|
||||
author: author ? author.name || author.username || author.publicEmail : authorName,
|
||||
updatedOn: authoredDate,
|
||||
}));
|
||||
|
||||
const filesWithData = files.map((file, index) => ({
|
||||
file: { ...file, ...metadata[index] },
|
||||
data: blobs[index],
|
||||
}));
|
||||
return filesWithData;
|
||||
};
|
||||
|
||||
listAllFiles = async (path: string, recursive = false, branch = this.branch) => {
|
||||
if (this.graphQLClient) {
|
||||
return await this.listAllFilesGraphQL(path, recursive, branch);
|
||||
}
|
||||
const entries = [];
|
||||
// eslint-disable-next-line prefer-const
|
||||
let { cursor, entries: initialEntries } = await this.fetchCursorAndEntries({
|
@ -3,7 +3,7 @@ import React, { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import AuthenticationPage from '../../components/UI/AuthenticationPage';
|
||||
import Icon from '../../components/UI/Icon';
|
||||
import { ImplicitAuthenticator, NetlifyAuthenticator, PkceAuthenticator } from '../../lib/auth';
|
||||
import { NetlifyAuthenticator, PkceAuthenticator } from '../../lib/auth';
|
||||
import { isNotEmpty } from '../../lib/util/string.util';
|
||||
|
||||
import type { MouseEvent } from 'react';
|
||||
@ -19,7 +19,6 @@ const LoginButtonIcon = styled(Icon)`
|
||||
|
||||
const clientSideAuthenticators = {
|
||||
pkce: (config: AuthenticatorConfig) => new PkceAuthenticator(config),
|
||||
implicit: (config: AuthenticatorConfig) => new ImplicitAuthenticator(config),
|
||||
} as const;
|
||||
|
||||
const GitLabAuthenticationPage = ({
|
@ -49,8 +49,6 @@ export default class GitLab implements BackendClass {
|
||||
apiRoot: string;
|
||||
token: string | null;
|
||||
mediaFolder?: string;
|
||||
useGraphQL: boolean;
|
||||
graphQLAPIRoot: string;
|
||||
|
||||
_mediaDisplayURLSem?: Semaphore;
|
||||
|
||||
@ -75,8 +73,6 @@ export default class GitLab implements BackendClass {
|
||||
this.apiRoot = config.backend.api_root || 'https://gitlab.com/api/v4';
|
||||
this.token = '';
|
||||
this.mediaFolder = config.media_folder;
|
||||
this.useGraphQL = config.backend.use_graphql || false;
|
||||
this.graphQLAPIRoot = config.backend.graphql_api_root || 'https://gitlab.com/api/graphql';
|
||||
this.lock = asyncLock();
|
||||
}
|
||||
|
||||
@ -112,8 +108,6 @@ export default class GitLab implements BackendClass {
|
||||
branch: this.branch,
|
||||
repo: this.repo,
|
||||
apiRoot: this.apiRoot,
|
||||
useGraphQL: this.useGraphQL,
|
||||
graphQLAPIRoot: this.graphQLAPIRoot,
|
||||
});
|
||||
const user = await this.api.user();
|
||||
const isCollab = await this.api.hasWriteAccess().catch((error: Error) => {
|
||||
@ -200,7 +194,7 @@ export default class GitLab implements BackendClass {
|
||||
getDifferences: (to, from) => this.api!.getDifferences(to, from),
|
||||
getFileId: path => this.api!.getFileId(path, this.branch),
|
||||
filterFile: file => this.filterFile(folder, file, extension, depth),
|
||||
customFetch: this.useGraphQL ? files => this.api!.readFilesGraphQL(files) : undefined,
|
||||
customFetch: undefined,
|
||||
});
|
||||
|
||||
return files;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user