├── .nvmrc ├── web └── docs │ ├── src │ ├── gatsby-theme-phony-docs │ │ └── logo.mdx │ ├── site-icon.png │ ├── site-image.jpg │ └── gatsby-plugin-theme-ui │ │ ├── components.js │ │ ├── headings.js │ │ ├── colors.js │ │ └── index.js │ ├── content │ ├── phonyql.mdx │ ├── index.mdx │ ├── introduction.mdx │ └── phonyql │ │ ├── config.mdx │ │ ├── cli.mdx │ │ ├── usage.mdx │ │ └── data.mdx │ ├── CHANGELOG.md │ ├── gatsby-config.js │ ├── package.json │ ├── .gitignore │ └── README.md ├── packages ├── utils │ ├── index.js │ ├── file.js │ ├── misc.js │ ├── array.js │ ├── number.js │ ├── object.js │ ├── string.js │ ├── src │ │ ├── object.ts │ │ ├── number.ts │ │ ├── constants.ts │ │ ├── types.ts │ │ ├── string.ts │ │ ├── index.ts │ │ ├── file.ts │ │ ├── misc.ts │ │ └── array.ts │ ├── README.md │ ├── ava.config.js │ ├── .nycrc │ ├── CHANGELOG.md │ ├── tsconfig.json │ ├── test │ │ ├── string.ts │ │ ├── number.ts │ │ ├── misc.ts │ │ └── array.ts │ ├── rollup.config.js │ └── package.json ├── graphql │ ├── index.js │ ├── export.js │ ├── flush.js │ ├── README.md │ ├── ava.config.js │ ├── CHANGELOG.md │ ├── src │ │ ├── utils │ │ │ ├── database.ts │ │ │ ├── serve.ts │ │ │ ├── type.ts │ │ │ ├── query.ts │ │ │ ├── types.ts │ │ │ ├── relations.ts │ │ │ ├── filter.ts │ │ │ ├── mutation.ts │ │ │ ├── root.ts │ │ │ ├── name.ts │ │ │ └── schema.ts │ │ ├── index.ts │ │ ├── flush.ts │ │ ├── export.ts │ │ └── constants.ts │ ├── tsconfig.json │ ├── .nycrc │ ├── test │ │ └── utils │ │ │ ├── type.ts │ │ │ ├── query.ts │ │ │ ├── relations.ts │ │ │ ├── filter.ts │ │ │ ├── mutation.ts │ │ │ └── name.ts │ ├── rollup.config.js │ └── package.json ├── server │ ├── index.js │ ├── README.md │ ├── src │ │ └── index.ts │ ├── CHANGELOG.md │ ├── tsconfig.json │ ├── package.json │ └── rollup.config.js └── cli │ ├── phonyql.js │ ├── README.md │ ├── CHANGELOG.md │ ├── tsconfig.json │ ├── src │ ├── explorer.ts │ ├── phonyql-cli.ts │ └── phonyql.ts │ ├── package.json │ └── rollup.config.js ├── .yarnrc ├── resources ├── logo.jpg └── logo.png ├── examples ├── apollo │ ├── empty.css │ ├── next-env.d.ts │ ├── constants │ │ └── root.ts │ ├── index.js │ ├── i18n.config.js │ ├── utils │ │ ├── table.ts │ │ ├── i18n.ts │ │ └── apollo.ts │ ├── i18n.js │ ├── CHANGELOG.md │ ├── theme │ │ └── index.ts │ ├── .gitignore │ ├── pages │ │ ├── _error.tsx │ │ ├── _app.tsx │ │ ├── _document.tsx │ │ └── index.tsx │ ├── next.config.js │ ├── phony.config.js │ ├── db.js │ ├── tsconfig.json │ ├── server.js │ ├── components │ │ ├── table.tsx │ │ └── icons.tsx │ ├── README.md │ └── package.json ├── cli │ ├── users.js │ ├── CHANGELOG.md │ ├── package.json │ ├── phony.config.js │ ├── db.js │ └── .gitignore └── graphql │ ├── CHANGELOG.md │ ├── index.js │ ├── package.json │ ├── phony.config.js │ ├── db.js │ └── .gitignore ├── .travis.yml ├── lerna.json ├── ava.config.js ├── utils └── rollup │ └── banner.js ├── .babelrc.js ├── CHANGELOG.md ├── .editorconfig ├── tsconfig.json ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── .codeclimate.yml ├── CONTRIBUTING.md ├── LICENSE ├── .gitignore ├── package.json ├── CODE_OF_CONDUCT.md └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | v12.16.1 2 | -------------------------------------------------------------------------------- /web/docs/src/gatsby-theme-phony-docs/logo.mdx: -------------------------------------------------------------------------------- 1 | Phony 2 | -------------------------------------------------------------------------------- /packages/utils/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./dist"); 2 | -------------------------------------------------------------------------------- /packages/graphql/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./dist"); 2 | -------------------------------------------------------------------------------- /packages/server/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./dist"); 2 | -------------------------------------------------------------------------------- /packages/utils/file.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./dist/file"); 2 | -------------------------------------------------------------------------------- /packages/utils/misc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./dist/misc"); 2 | -------------------------------------------------------------------------------- /packages/graphql/export.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./dist/export"); 2 | -------------------------------------------------------------------------------- /packages/graphql/flush.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./dist/flush"); 2 | -------------------------------------------------------------------------------- /packages/utils/array.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./dist/array"); 2 | -------------------------------------------------------------------------------- /packages/utils/number.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./dist/number"); 2 | -------------------------------------------------------------------------------- /packages/utils/object.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./dist/object"); 2 | -------------------------------------------------------------------------------- /packages/utils/string.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./dist/string"); 2 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | workspaces-experimental "true" 2 | registry "https://registry.npmjs.org/" 3 | -------------------------------------------------------------------------------- /resources/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixelass/phony/HEAD/resources/logo.jpg -------------------------------------------------------------------------------- /resources/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixelass/phony/HEAD/resources/logo.png -------------------------------------------------------------------------------- /examples/apollo/empty.css: -------------------------------------------------------------------------------- 1 | /* 2 | This is needed so "next/css" works correctly. 3 | */ 4 | -------------------------------------------------------------------------------- /packages/cli/phonyql.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | module.exports = require("./dist/phonyql"); 3 | -------------------------------------------------------------------------------- /web/docs/src/site-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixelass/phony/HEAD/web/docs/src/site-icon.png -------------------------------------------------------------------------------- /web/docs/src/site-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixelass/phony/HEAD/web/docs/src/site-image.jpg -------------------------------------------------------------------------------- /examples/apollo/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /packages/utils/src/object.ts: -------------------------------------------------------------------------------- 1 | export function stringify(data, space = 4) { 2 | return JSON.stringify(data, null, space); 3 | } 4 | -------------------------------------------------------------------------------- /examples/apollo/constants/root.ts: -------------------------------------------------------------------------------- 1 | export const APOLLO_STATE = "__APOLLO_STATE__"; 2 | export const GRAPHQL_URI = process.env.graphqlUri; 3 | -------------------------------------------------------------------------------- /examples/apollo/index.js: -------------------------------------------------------------------------------- 1 | const {setConfig} = require("next/config"); 2 | setConfig(require("./next.config")); 3 | 4 | require("./server"); 5 | -------------------------------------------------------------------------------- /web/docs/src/gatsby-plugin-theme-ui/components.js: -------------------------------------------------------------------------------- 1 | import Prism from '@theme-ui/prism'; 2 | export default { 3 | pre: props => props.children, 4 | code: Prism 5 | }; 6 | -------------------------------------------------------------------------------- /examples/apollo/i18n.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | defaultLanguage: "en", 3 | otherLanguages: ["de"], 4 | localePath: !process.browser ? "public/locales" : "locales", 5 | }; 6 | -------------------------------------------------------------------------------- /packages/cli/README.md: -------------------------------------------------------------------------------- 1 | # Phony cli 2 | 3 | This is a package for the `phony` mono-repository. 4 | For user docs see [pixelass.github.io/phony](https://pixelass.github.io/phony) 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "12" 4 | os: linux 5 | dist: xenial 6 | install: 7 | - yarn 8 | script: 9 | - yarn test 10 | - codecov --disable=gcov 11 | -------------------------------------------------------------------------------- /packages/server/README.md: -------------------------------------------------------------------------------- 1 | # Phony server 2 | 3 | This is a package for the `phony` mono-repository. 4 | For user docs see [pixelass.github.io/phony](https://pixelass.github.io/phony) 5 | -------------------------------------------------------------------------------- /packages/utils/README.md: -------------------------------------------------------------------------------- 1 | # Phony utils 2 | 3 | This is a package for the `phony` mono-repository. 4 | For user docs see [pixelass.github.io/phony](https://pixelass.github.io/phony) 5 | -------------------------------------------------------------------------------- /packages/graphql/README.md: -------------------------------------------------------------------------------- 1 | # Phony graphql 2 | 3 | This is a package for the `phony` mono-repository. 4 | For user docs see [pixelass.github.io/phony](https://pixelass.github.io/phony) 5 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "examples/*", 4 | "packages/*", 5 | "web/*" 6 | ], 7 | "version": "1.0.1", 8 | "npmClient": "yarn", 9 | "useWorkspaces": true 10 | } 11 | -------------------------------------------------------------------------------- /examples/apollo/utils/table.ts: -------------------------------------------------------------------------------- 1 | export const withSortOrder = (columns, {sortField, sortOrder}) => 2 | columns.map(column => ({ 3 | ...column, 4 | defaultSort: sortField === column.field ? sortOrder : undefined 5 | })); 6 | -------------------------------------------------------------------------------- /packages/utils/src/number.ts: -------------------------------------------------------------------------------- 1 | export function isFloat(n: number): boolean { 2 | return n === +n && n !== (n | 0); 3 | } 4 | 5 | export function isInteger(n: number): boolean { 6 | return n === +n && n === (n | 0); 7 | } 8 | -------------------------------------------------------------------------------- /ava.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | "typescript": { 3 | "rewritePaths": { 4 | "src/": "dist/" 5 | }, 6 | "extensions": [ 7 | "ts" 8 | ], 9 | }, 10 | "require": [ 11 | "ts-node/register" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/utils/ava.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | "typescript": { 3 | "rewritePaths": { 4 | "src/": "dist/" 5 | }, 6 | "extensions": [ 7 | "ts" 8 | ], 9 | }, 10 | "require": [ 11 | "ts-node/register" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/graphql/ava.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | "typescript": { 3 | "rewritePaths": { 4 | "src/": "dist/" 5 | }, 6 | "extensions": [ 7 | "ts" 8 | ], 9 | }, 10 | "require": [ 11 | "ts-node/register" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/server/src/index.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import cors from "cors"; 3 | 4 | function createServer() { 5 | const app = express(); 6 | app.use(cors()); 7 | return app; 8 | } 9 | 10 | export default createServer; 11 | -------------------------------------------------------------------------------- /examples/apollo/i18n.js: -------------------------------------------------------------------------------- 1 | /* 2 | Do not copy/paste this file. It is used internally 3 | to manage end-to-end test suites. 4 | */ 5 | 6 | const {default: NextI18Next} = require("next-i18next"); 7 | const i18nConfig = require("./i18n.config"); 8 | module.exports = new NextI18Next(i18nConfig); 9 | -------------------------------------------------------------------------------- /examples/apollo/utils/i18n.ts: -------------------------------------------------------------------------------- 1 | import NextI18Next from "next-i18next"; 2 | import i18nConfig from "../i18n.config"; 3 | 4 | const NextI18NextInstance = new NextI18Next(i18nConfig); 5 | 6 | export const {appWithTranslation, i18n, useTranslation, withTranslation, Link} = NextI18NextInstance; 7 | -------------------------------------------------------------------------------- /web/docs/content/phonyql.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'phonyql' 3 | description: 'Command line interface for a phony graphql service' 4 | --- 5 | 6 | ## Installation 7 | 8 | ```bash 9 | yarn add @phony/cli @phony/graphql 10 | ``` 11 | 12 | ## Usage 13 | 14 | ```bash 15 | phonyql 16 | ``` 17 | -------------------------------------------------------------------------------- /examples/cli/users.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | users: [ 3 | { 4 | name: "John Doe", 5 | id: 123, 6 | created: new Date("2020-01-01"), 7 | updated: new Date("2020-02-01") 8 | }, 9 | { 10 | name: "Jane Doe", 11 | id: 456, 12 | created: new Date("2020-01-01") 13 | } 14 | ] 15 | }; 16 | -------------------------------------------------------------------------------- /packages/utils/.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "check-coverage": true, 3 | "all": true, 4 | "extension": [".js", ".ts"], 5 | "include": ["src/**/*.ts"], 6 | "exclude": ["src/file.ts", "src/object.ts", "src/types.ts", "src/constants.ts", "src/index.ts"], 7 | "reporter": ["html", "lcov", "text", "text-summary"], 8 | "report-dir": "coverage" 9 | } 10 | -------------------------------------------------------------------------------- /web/docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.1](https://github.com/pixelass/phony/compare/v1.0.0...v1.0.1) (2020-03-11) 7 | 8 | **Note:** Version bump only for package docs 9 | -------------------------------------------------------------------------------- /examples/cli/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.1](https://github.com/pixelass/phony/compare/v1.0.0...v1.0.1) (2020-03-11) 7 | 8 | **Note:** Version bump only for package @examples/cli 9 | -------------------------------------------------------------------------------- /packages/cli/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.1](https://github.com/pixelass/phony/compare/v1.0.0...v1.0.1) (2020-03-11) 7 | 8 | **Note:** Version bump only for package @phony/cli 9 | -------------------------------------------------------------------------------- /packages/utils/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.1](https://github.com/pixelass/phony/compare/v1.0.0...v1.0.1) (2020-03-11) 7 | 8 | **Note:** Version bump only for package @phony/utils 9 | -------------------------------------------------------------------------------- /examples/apollo/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.1](https://github.com/pixelass/phony/compare/v1.0.0...v1.0.1) (2020-03-11) 7 | 8 | **Note:** Version bump only for package @examples/apollo 9 | -------------------------------------------------------------------------------- /packages/server/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.1](https://github.com/pixelass/phony/compare/v1.0.0...v1.0.1) (2020-03-11) 7 | 8 | **Note:** Version bump only for package @phony/server 9 | -------------------------------------------------------------------------------- /examples/graphql/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.1](https://github.com/pixelass/phony/compare/v1.0.0...v1.0.1) (2020-03-11) 7 | 8 | **Note:** Version bump only for package @examples/graphql 9 | -------------------------------------------------------------------------------- /packages/utils/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const ID = "id"; 2 | export const ID_SUFFIX = `_${ID}`; 3 | export const ID_PATTERN = new RegExp(`${ID_SUFFIX}$`); 4 | export const NL = "\n"; 5 | export const NNL = "\n\n"; 6 | export const __ = " "; 7 | export const NL__ = `${NL}${__}`; 8 | export enum OPERATORS { 9 | gt = "gt", 10 | gte = "gte", 11 | lt = "lt", 12 | lte = "lte" 13 | } 14 | -------------------------------------------------------------------------------- /examples/graphql/index.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const createGraphqlServer = require("@phony/graphql"); 3 | const flush = require("@phony/graphql/flush"); 4 | const config = require("./phony.config"); 5 | 6 | (async function start() { 7 | const db = require(path.resolve(process.cwd(), config.database)); 8 | await flush(db, config); 9 | createGraphqlServer(db, config); 10 | })(); 11 | -------------------------------------------------------------------------------- /web/docs/content/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Phony' 3 | description: 'Welcome to Phony' 4 | --- 5 | 6 | Phony services for not so phony development. 7 | 8 | * [CLI](https://pixelass.github.io/phony/phonyql/cli) 9 | * [Config](https://pixelass.github.io/phony/phonyql/config) 10 | * [Data](https://pixelass.github.io/phony/phonyql/data) 11 | * [Usage](https://pixelass.github.io/phony/phonyql/usage) 12 | -------------------------------------------------------------------------------- /utils/rollup/banner.js: -------------------------------------------------------------------------------- 1 | module.exports = ({name, version, author, repository, license}) => `/*! 2 | * Copyright (c) ${typeof author === "object" ? author.name : author} 3 | * @author ${typeof author === "object" ? author.name : author} 4 | * @license ${license} 5 | * @name ${name} 6 | * @version ${version} 7 | *${repository ? ` @see ${typeof repository === "object" ? repository.url : repository}\n */` : "/"}`; 8 | -------------------------------------------------------------------------------- /.babelrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@babel/preset-env", "@babel/preset-typescript"], 3 | plugins: [ 4 | "@babel/plugin-proposal-class-properties", 5 | "@babel/plugin-proposal-object-rest-spread", 6 | [ 7 | "babel-plugin-transform-inline-environment-variables", 8 | { 9 | include: ["NODE_ENV"] 10 | } 11 | ] 12 | ], 13 | env: { 14 | test: { 15 | plugins: ["babel-plugin-istanbul"] 16 | } 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /examples/apollo/theme/index.ts: -------------------------------------------------------------------------------- 1 | import createMuiTheme from "@material-ui/core/styles/createMuiTheme"; 2 | import responsiveFontSizes from "@material-ui/core/styles/responsiveFontSizes"; 3 | import indigo from "@material-ui/core/colors/indigo"; 4 | import pink from "@material-ui/core/colors/pink"; 5 | 6 | export const theme = responsiveFontSizes(createMuiTheme({ 7 | palette: { 8 | primary: indigo, 9 | secondary: pink 10 | } 11 | })); 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.1](https://github.com/pixelass/phony/compare/v1.0.0...v1.0.1) (2020-03-11) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **graphql:** getPage -> withPagination naming issue ([d092582](https://github.com/pixelass/phony/commit/d092582f0879f48d10ed287a9c046fdf14caa338)) 12 | -------------------------------------------------------------------------------- /examples/apollo/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | .env* 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | ## build files 28 | schema.graphql 29 | db.json 30 | -------------------------------------------------------------------------------- /packages/graphql/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.1](https://github.com/pixelass/phony/compare/v1.0.0...v1.0.1) (2020-03-11) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **graphql:** getPage -> withPagination naming issue ([d092582](https://github.com/pixelass/phony/commit/d092582f0879f48d10ed287a9c046fdf14caa338)) 12 | -------------------------------------------------------------------------------- /packages/graphql/src/utils/database.ts: -------------------------------------------------------------------------------- 1 | import { stingify, writeFile } from "@phony/utils"; 2 | import { Database } from "./types"; 3 | 4 | export async function update(data: Database, filePath: string): Promise { 5 | return await writeFile(filePath, stingify(data)) 6 | .then(() => { 7 | console.log(`database successfully updated at ${filePath}`); 8 | return true; 9 | }) 10 | .catch(error => { 11 | console.error(error); 12 | return false; 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = tab 6 | indent_size = 4 7 | max_line_length = 100 8 | end_of_line = lf 9 | trim_trailing_spaces = true 10 | insert_final_newline = true 11 | 12 | # trailing spaces in markdown indicate word wrap 13 | [*.md] 14 | trim_trailing_spaces = false 15 | 16 | # Set indents 17 | [*.{json,md,yml}] 18 | indent_size = 2 19 | 20 | # Set spaces 21 | [*.{json,md,yml}] 22 | indent_style = space 23 | 24 | [.*rc] 25 | indent_style = space 26 | indent_size = 2 27 | -------------------------------------------------------------------------------- /packages/graphql/src/index.ts: -------------------------------------------------------------------------------- 1 | import { buildSchema } from "graphql"; 2 | import phonyServe from "./utils/serve"; 3 | import { Database, PonyConfig } from "./utils/types"; 4 | import { build } from "./utils/schema"; 5 | 6 | async function serve(json: Database, config: PonyConfig): Promise { 7 | const { schema, rootValue } = await build(json, config); 8 | return await phonyServe( 9 | { 10 | schema: buildSchema(schema), 11 | rootValue 12 | }, 13 | config.port 14 | ); 15 | } 16 | 17 | export default serve; 18 | -------------------------------------------------------------------------------- /examples/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@examples/cli", 3 | "version": "1.0.1", 4 | "private": true, 5 | "description": "Example for the phony cli", 6 | "repository": "git@github.com:pixelass/phony.git", 7 | "license": "MIT", 8 | "author": { 9 | "name": "Gregor Adams", 10 | "email": "greg@pixelass.com", 11 | "url": "https://pixelass.com" 12 | }, 13 | "scripts": { 14 | "export": "phonyql -e", 15 | "start": "phonyql -f" 16 | }, 17 | "devDependencies": { 18 | "@phony/cli": "^1.0.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/graphql/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@examples/graphql", 3 | "version": "1.0.1", 4 | "private": true, 5 | "description": "Example for a phonyql", 6 | "repository": "git@github.com:pixelass/phony.git", 7 | "license": "MIT", 8 | "author": { 9 | "name": "Gregor Adams", 10 | "email": "greg@pixelass.com", 11 | "url": "https://pixelass.com" 12 | }, 13 | "main": "index.js", 14 | "scripts": { 15 | "start": "node index.js" 16 | }, 17 | "devDependencies": { 18 | "@phony/graphql": "^1.0.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /web/docs/gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | pathPrefix: `/phony`, 3 | siteMetadata: { 4 | title: `Phony`, 5 | name: `Phony`, 6 | siteUrl: `https://pixelass.gihub.io/phony`, 7 | description: `Phony service. mock graphql json-server`, 8 | social: [ 9 | { 10 | name: `github`, 11 | url: `https://github.com/pixelass/phony` 12 | } 13 | ], 14 | sidebarConfig: { 15 | forcedNavOrder: ["/introduction", "/phonyql"], 16 | ignoreIndex: true 17 | } 18 | }, 19 | plugins: [{ resolve: `gatsby-theme-phony-docs` }] 20 | }; 21 | -------------------------------------------------------------------------------- /examples/apollo/pages/_error.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import {useTranslation} from "../utils/i18n"; 3 | import {Typography} from "@material-ui/core"; 4 | 5 | const Error = ({statusCode}) => { 6 | const {t} = useTranslation("error"); 7 | return {t(statusCode)}; 8 | }; 9 | 10 | Error.getInitialProps = ({res, err}) => { 11 | const statusCode = res ? res.statusCode : err ? err.statusCode : 404; 12 | return { 13 | namespacesRequired: ["error"], 14 | statusCode 15 | }; 16 | }; 17 | 18 | export default Error; 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "skipLibCheck": true, 5 | "alwaysStrict": true, 6 | "declaration": true, 7 | "esModuleInterop": true, 8 | "importHelpers": true, 9 | "lib": ["dom", "es7"], 10 | "module": "commonjs", 11 | "moduleResolution": "node", 12 | "jsx": "react", 13 | "outDir": "dist", 14 | "declarationDir": "dist", 15 | "sourceMap": true, 16 | "strict": false, 17 | "target": "es6" 18 | }, 19 | "exclude": [ 20 | "node_modules" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "skipLibCheck": true, 5 | "alwaysStrict": true, 6 | "declaration": true, 7 | "esModuleInterop": true, 8 | "importHelpers": true, 9 | "lib": ["dom", "es7"], 10 | "module": "commonjs", 11 | "moduleResolution": "node", 12 | "jsx": "react", 13 | "outDir": "dist", 14 | "declarationDir": "dist", 15 | "sourceMap": true, 16 | "strict": false, 17 | "target": "es6" 18 | }, 19 | "exclude": [ 20 | "node_modules" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /examples/apollo/utils/apollo.ts: -------------------------------------------------------------------------------- 1 | import {ApolloClient, HttpLink, InMemoryCache} from "@apollo/client"; 2 | import fetch from "isomorphic-unfetch"; 3 | import {APOLLO_STATE, GRAPHQL_URI} from "../constants/root"; 4 | const initCache = (initialState?: any) => new InMemoryCache().restore(initialState || {}); 5 | 6 | const client = new ApolloClient({ 7 | cache: initCache(typeof window !== "undefined" ? window[APOLLO_STATE] : undefined), 8 | ssrMode: true, 9 | link: new HttpLink({ 10 | // @ts-ignore 11 | fetch, 12 | uri: GRAPHQL_URI 13 | }) 14 | }); 15 | 16 | export default client; 17 | -------------------------------------------------------------------------------- /packages/graphql/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "skipLibCheck": true, 5 | "alwaysStrict": true, 6 | "declaration": true, 7 | "esModuleInterop": true, 8 | "importHelpers": true, 9 | "lib": ["dom", "es7"], 10 | "module": "commonjs", 11 | "moduleResolution": "node", 12 | "jsx": "react", 13 | "outDir": "dist", 14 | "declarationDir": "dist", 15 | "sourceMap": true, 16 | "strict": false, 17 | "target": "es6" 18 | }, 19 | "exclude": [ 20 | "node_modules" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /packages/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "skipLibCheck": true, 5 | "alwaysStrict": true, 6 | "declaration": true, 7 | "esModuleInterop": true, 8 | "importHelpers": true, 9 | "lib": ["dom", "es7"], 10 | "module": "commonjs", 11 | "moduleResolution": "node", 12 | "jsx": "react", 13 | "outDir": "dist", 14 | "declarationDir": "dist", 15 | "sourceMap": true, 16 | "strict": false, 17 | "target": "es6" 18 | }, 19 | "exclude": [ 20 | "node_modules" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /packages/utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "skipLibCheck": true, 5 | "alwaysStrict": true, 6 | "declaration": true, 7 | "esModuleInterop": true, 8 | "importHelpers": true, 9 | "lib": ["dom", "es7"], 10 | "module": "commonjs", 11 | "moduleResolution": "node", 12 | "jsx": "react", 13 | "outDir": "dist", 14 | "declarationDir": "dist", 15 | "sourceMap": true, 16 | "strict": false, 17 | "target": "es6" 18 | }, 19 | "exclude": [ 20 | "node_modules" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /web/docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "1.0.1", 4 | "license": "MIT", 5 | "private": true, 6 | "author": { 7 | "name": "Gregor Adams", 8 | "email": "greg@pixelass.com", 9 | "url": "https://pixelass.com" 10 | }, 11 | "scripts": { 12 | "docs:build": "gatsby build --prefix-paths", 13 | "docs:develop": "gatsby develop" 14 | }, 15 | "dependencies": { 16 | "@types/react": "^16.9.23", 17 | "gatsby": "^2.19.28", 18 | "gatsby-theme-phony-docs": "1.0.0", 19 | "react": "^16.13.0", 20 | "react-dom": "^16.13.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/graphql/.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "all": true, 3 | "check-coverage": true, 4 | "exclude": [ 5 | "src/utils/types.ts", 6 | "src/export.ts", 7 | "src/flush.ts", 8 | "src/index.ts", 9 | "src/constants.ts", 10 | "src/utils/database.ts", 11 | "src/utils/root.ts", 12 | "src/utils/schema.ts", 13 | "src/utils/serve.ts" 14 | ], 15 | "extension": [ 16 | ".js", 17 | ".ts" 18 | ], 19 | "include": [ 20 | "src/**/*.ts" 21 | ], 22 | "report-dir": "coverage", 23 | "reporter": [ 24 | "html", 25 | "lcov", 26 | "text", 27 | "text-summary" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /packages/graphql/src/flush.ts: -------------------------------------------------------------------------------- 1 | import { writeFile, stringify } from "@phony/utils"; 2 | import {Database, PonyConfig} from "./utils/types"; 3 | import path from "path"; 4 | import {CWD} from "./constants"; 5 | 6 | async function flushData(data: Database, config: PonyConfig): Promise { 7 | const filePath = path.resolve(CWD, config.database); 8 | return writeFile(filePath, stringify(data)) 9 | .then(() => { 10 | console.log(`database successfully flushed at ${filePath}`); 11 | return true; 12 | }) 13 | .catch(error => { 14 | console.error(error); 15 | return false; 16 | }); 17 | } 18 | 19 | export default flushData; 20 | -------------------------------------------------------------------------------- /examples/apollo/next.config.js: -------------------------------------------------------------------------------- 1 | const Mode = require("frontmatter-markdown-loader/mode"); 2 | const withCSS = require("@zeit/next-css"); 3 | const {config} = require("dotenv"); 4 | config(); 5 | 6 | module.exports = withCSS({ 7 | transpileModules: ["(.*)"], 8 | env: { 9 | graphqlUri: process.env.GRAPHQL_URI 10 | }, 11 | webpack: (config, options) => { 12 | config.module.rules.push({ 13 | test: /\.md$/, 14 | use: [ 15 | options.defaultLoaders.babel, 16 | { 17 | loader: "frontmatter-markdown-loader", 18 | options: { 19 | mode: [Mode.BODY] 20 | } 21 | } 22 | ] 23 | }); 24 | return config; 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /web/docs/content/introduction.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Introduction" 3 | description: "phony services for lazy development" 4 | --- 5 | 6 | ## Why Phony? 7 | 8 | Phony was inspired by [json-graphql-server](https://github.com/marmelab/json-graphql-server). 9 | Its sole purpose is to provide a mock graphql service. 10 | 11 | > Warning! This might not work for you especially if your grapqhl schema does not match 12 | > this mock. 13 | 14 | ### When should I use this? 15 | 16 | - to create a simple mvp with graphql 17 | - to build custom static site builders 18 | - when using the same schema in production 19 | - in test environments 20 | - in dev environments 21 | -------------------------------------------------------------------------------- /examples/cli/phony.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | queryConfig: { 3 | get: { 4 | byId: "[Name]", 5 | all: "all[Name]s", 6 | meta: "_all[Names]Meta" 7 | }, 8 | post: { 9 | create: "create[Name]", 10 | update: "update[Name]", 11 | delete: "remove[Name]" 12 | }, 13 | input: { 14 | filter: "[Names]Filter", 15 | filterFields: "[Names]FilterFields", 16 | create: "[Name]InitInput", 17 | update: "[Name]UpdateInput" 18 | }, 19 | internalFields: { 20 | created: "created", 21 | updated: "updated", 22 | views: "views" 23 | } 24 | }, 25 | schema: "schema.graphql", 26 | input: "db.js", 27 | database: "db.json", 28 | port: 1337 29 | } 30 | -------------------------------------------------------------------------------- /examples/graphql/phony.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | queryConfig: { 3 | get: { 4 | byId: "[Name]", 5 | all: "all[Name]s", 6 | meta: "_all[Names]Meta" 7 | }, 8 | post: { 9 | create: "create[Name]", 10 | update: "update[Name]", 11 | delete: "remove[Name]" 12 | }, 13 | input: { 14 | filter: "[Names]Filter", 15 | filterFields: "[Names]FilterFields", 16 | create: "[Name]InitInput", 17 | update: "[Name]UpdateInput" 18 | }, 19 | internalFields: { 20 | created: "created", 21 | updated: "updated", 22 | views: "views" 23 | } 24 | }, 25 | schema: "schema.graphql", 26 | input: "db.js", 27 | database: "db.json", 28 | port: 1337 29 | } 30 | -------------------------------------------------------------------------------- /examples/apollo/phony.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | queryConfig: { 3 | get: { 4 | byId: "get[Name]", 5 | all: "get[Name]s", 6 | meta: "_get[Names]Meta" 7 | }, 8 | post: { 9 | create: "create[Name]", 10 | update: "update[Name]", 11 | delete: "delete[Name]" 12 | }, 13 | input: { 14 | filter: "[Names]Filter", 15 | filterFields: "[Names]FilterFields", 16 | create: "[Name]InitInput", 17 | update: "[Name]UpdateInput" 18 | }, 19 | internalFields: { 20 | created: "created", 21 | updated: "updated", 22 | views: "views" 23 | } 24 | }, 25 | schema: "schema.graphql", 26 | input: "db.js", 27 | database: "db.json", 28 | port: 4000 29 | } 30 | -------------------------------------------------------------------------------- /examples/apollo/db.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | items: [ 3 | { 4 | id: "0000", 5 | name: "Item 1", 6 | created: new Date(), 7 | updated: new Date(), 8 | description: "This is an item", 9 | price: 10.54, 10 | stock: 300 11 | }, 12 | { 13 | id: "0001", 14 | name: "Item 2", 15 | created: new Date(), 16 | price: 21.31, 17 | stock: 100 18 | }, 19 | ... (new Array(300).fill(Boolean).map((x, i) => ({ 20 | id: `${i + 2}`.padStart(4, "0"), 21 | name: `Item ${i + 3}`, 22 | created: new Date(`2019-${i%12 + 1}-${i%28 + 1}`), 23 | price: Math.floor(Math.random() * 1000) / 100, 24 | stock: Math.floor(Math.random() * 1000) 25 | }))) 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /packages/utils/src/types.ts: -------------------------------------------------------------------------------- 1 | export type SortField = string; 2 | export type SortOrder = "asc" | "desc"; 3 | 4 | export interface Sorting { 5 | field: SortField; 6 | order: SortOrder; 7 | } 8 | 9 | export interface Pagination { 10 | page: number; 11 | pageSize: number; 12 | } 13 | 14 | export interface Entry { 15 | [key: string]: string | number; 16 | } 17 | 18 | export interface Item extends Entry { 19 | id: string; 20 | } 21 | 22 | export type Collection = Item[]; 23 | 24 | export interface Fields { 25 | [key: string]: string; 26 | } 27 | 28 | export interface Filter { 29 | fields?: Fields; 30 | q?: string; 31 | } 32 | 33 | export type ItemComparison = (item: Item) => boolean; 34 | -------------------------------------------------------------------------------- /examples/apollo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "skipLibCheck": true, 10 | "strict": false, 11 | "allowSyntheticDefaultImports": true, 12 | "noEmit": true, 13 | "esModuleInterop": true, 14 | "module": "esnext", 15 | "moduleResolution": "node", 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "jsx": "preserve", 19 | "allowJs": true, 20 | "forceConsistentCasingInFileNames": true 21 | }, 22 | "include": [ 23 | "**/*.ts", 24 | "**/*.tsx" 25 | ], 26 | "exclude": [ 27 | "node_modules" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | exclude_patterns: 3 | - ".*" 4 | - "web" 5 | - "examples" 6 | - "utils" 7 | - "*.lock" 8 | - "*.log" 9 | - "*.json" 10 | - "*.yml" 11 | - "**/*.d.ts" 12 | - "**/*.config.js" 13 | - "**/.*rc" 14 | - "**/.*rc.js" 15 | - "**/.nyc-output" 16 | - "**/.rpt2_cache" 17 | - "**/node_modules" 18 | - "**/test" 19 | - "**/lib" 20 | - "**/dist" 21 | - "**/public" 22 | - "**/coverage" 23 | - "**/docs" 24 | checks: 25 | method-lines: 26 | config: 27 | threshold: 40 28 | file-lines: 29 | config: 30 | threshold: 300 31 | plugins: 32 | editorconfig: 33 | enabled: true 34 | fixme: 35 | enabled: true 36 | git-legal: 37 | enabled: true 38 | -------------------------------------------------------------------------------- /packages/graphql/src/export.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import { writeFile } from "@phony/utils"; 3 | import { Database, PonyConfig } from "./utils/types"; 4 | import { build } from "./utils/schema"; 5 | import { CWD } from "./constants"; 6 | 7 | async function exportSchema(json: Database, config: PonyConfig): Promise { 8 | const { schema } = await build(json, config); 9 | const filePath = path.resolve(CWD, config.schema); 10 | return await writeFile(filePath, schema) 11 | .then(() => { 12 | console.log(`schema has been exported to ${filePath}`); 13 | return true; 14 | }) 15 | .catch(error => { 16 | console.error(error); 17 | return false; 18 | }); 19 | } 20 | 21 | export default exportSchema; 22 | -------------------------------------------------------------------------------- /examples/apollo/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const next = require("next"); 3 | const {default: nextI18NextMiddleware} = require("next-i18next/middleware"); 4 | 5 | 6 | const nextI18next = require("./i18n"); 7 | const port = process.env.PORT || 3000; 8 | const app = next({dev: process.env.NODE_ENV !== "production"}); 9 | const handle = app.getRequestHandler(); 10 | 11 | const handler = (req, res) => handle(req, res); 12 | 13 | (async () => { 14 | await app.prepare(); 15 | const server = express(); 16 | server.use(nextI18NextMiddleware(nextI18next)); 17 | server.get("*", handler); 18 | await server.listen(port); 19 | console.log(`Client ready on http://localhost:${port}`); // eslint-disable-line no-console 20 | })(); 21 | -------------------------------------------------------------------------------- /packages/graphql/src/constants.ts: -------------------------------------------------------------------------------- 1 | import { __, ID_SUFFIX } from "@phony/utils"; 2 | 3 | export const TYPES = { 4 | id: "ID", 5 | object: "JSON", 6 | json: "JSON", 7 | string: "String", 8 | date: "String", 9 | int: "Int", 10 | float: "Float" 11 | }; 12 | 13 | export const ID_SUFFIX_PATTERN = new RegExp(`${ID_SUFFIX}$`); 14 | 15 | export const META_DATA_TYPE = `type MetaData { 16 | ${__}count: Int! 17 | }`; 18 | 19 | export const PAGINATION_TYPE = `input Pagination { 20 | ${__}page: Int 21 | ${__}pageSize: Int 22 | }`; 23 | 24 | export const SORTING_TYPE = `input Sorting { 25 | ${__}field: String! 26 | ${__}order: SortOrder! 27 | } 28 | 29 | enum SortOrder { 30 | ${__}asc 31 | ${__}desc 32 | }`; 33 | 34 | export const CWD = process.cwd(); 35 | -------------------------------------------------------------------------------- /packages/utils/src/string.ts: -------------------------------------------------------------------------------- 1 | import pluralize from "pluralize"; 2 | 3 | export function capital(str: string): string { 4 | return `${str[0].toUpperCase()}${str.slice(1)}`; 5 | } 6 | 7 | export function isCapital(s: string) { 8 | return s[0] === s[0].toUpperCase() 9 | } 10 | 11 | /* istanbul ignore next */ 12 | export function plural(s: string) { 13 | return pluralize.plural(s) 14 | } 15 | 16 | /* istanbul ignore next */ 17 | export function singular(s: string) { 18 | return pluralize.singular(s) 19 | } 20 | 21 | /* istanbul ignore next */ 22 | export function isPlural(s: string) { 23 | return pluralize.isPlural(s) 24 | } 25 | 26 | /* istanbul ignore next */ 27 | export function isSingular(s: string) { 28 | return pluralize.isSingular(s) 29 | } 30 | -------------------------------------------------------------------------------- /packages/utils/src/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | isNull, 3 | isArray, 4 | isDate, 5 | isObject, 6 | isNumber, 7 | isNullOrUndefined, 8 | isUndefined, 9 | isBoolean, 10 | isPlainObject, 11 | isString, 12 | isEmptyString 13 | } from "is-what"; 14 | export { hasMatch, compare, sortByField, withSorting, withPagination, withFilter } from "./array"; 15 | export { ID, ID_SUFFIX,ID_PATTERN, NNL, NL, NL__, __, OPERATORS} from "./constants"; 16 | export { readFile, writeFile } from "./file"; 17 | export { isRelative, isRelated, isSame, isId, withRequired, withoutArray, withArray } from "./misc"; 18 | export { isFloat, isInteger } from "./number"; 19 | export { stringify } from "./object"; 20 | export { isCapital, capital, isPlural, plural, isSingular, singular } from "./string"; 21 | export * from "./types"; 22 | 23 | -------------------------------------------------------------------------------- /packages/utils/src/file.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | import mkdirp from "mkdirp"; 4 | import pify from "pify"; 5 | 6 | 7 | const fsp = { 8 | readFile: pify(fs.readFile), 9 | writeFile: pify(fs.writeFile) 10 | }; 11 | 12 | export function writeFile(filePath: string, content: string): Promise { 13 | const { dir } = path.parse(filePath); 14 | return new Promise((resolve, reject) => { 15 | mkdirp(dir) 16 | .then(() => { 17 | fsp.writeFile(filePath, content) 18 | .then(() => resolve(true)) 19 | .catch(error => { 20 | reject(error); 21 | }); 22 | }) 23 | .catch(error => { 24 | reject(error); 25 | }); 26 | }); 27 | } 28 | 29 | export function readFile(filePath: string): Promise { 30 | return fsp.readFile(filePath, "utf-8"); 31 | } 32 | -------------------------------------------------------------------------------- /packages/cli/src/explorer.ts: -------------------------------------------------------------------------------- 1 | import { cosmiconfig } from "cosmiconfig"; 2 | 3 | export interface PonyConfig { 4 | queryConfig: { 5 | get: { 6 | byId: string; 7 | all: string; 8 | meta: string; 9 | }; 10 | post: { 11 | create: string; 12 | update: string; 13 | delete: string; 14 | }; 15 | input: { 16 | filter: string; 17 | filterFields: string; 18 | create: string; 19 | update: string; 20 | }; 21 | internalFields: { 22 | created: string; 23 | updated: string; 24 | views: string; 25 | }; 26 | }; 27 | schema: string; 28 | input: string; 29 | database: string; 30 | port: string | number; 31 | } 32 | 33 | function explorer(): Promise<{ config: any; filepath: string; isEmpty?: boolean }> { 34 | return cosmiconfig("phony").search(); 35 | } 36 | 37 | export default explorer; 38 | -------------------------------------------------------------------------------- /packages/graphql/src/utils/serve.ts: -------------------------------------------------------------------------------- 1 | import createServer from "@phony/server"; 2 | import graphqlHTTP from "express-graphql"; 3 | import { GraphQLSchema } from "graphql"; 4 | 5 | interface ServeOptions { 6 | schema: GraphQLSchema; 7 | rootValue: unknown; 8 | } 9 | export default async function serve( 10 | { schema, rootValue }: ServeOptions, 11 | port: string | number = 1337 12 | ): Promise { 13 | const app = createServer(); 14 | app.get( 15 | "*", 16 | graphqlHTTP({ 17 | schema, 18 | rootValue, 19 | graphiql: true 20 | }) 21 | ); 22 | app.post( 23 | "*", 24 | graphqlHTTP({ 25 | schema, 26 | rootValue, 27 | graphiql: false 28 | }) 29 | ); 30 | await app.listen(port); 31 | console.log(`Client ready on http://localhost:${port}`); // eslint-disable-line no-console 32 | return true; 33 | } 34 | -------------------------------------------------------------------------------- /packages/utils/test/string.ts: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import {isCapital, capital} from "../src/string"; 3 | 4 | test("isCapital() returns `true` for capital strings", t => { 5 | t.true(isCapital("Hello")); 6 | t.true(isCapital("HELLO")); 7 | t.true(isCapital("HellO")); 8 | }); 9 | 10 | test("isCapital() returns `false` for non capital strings", t => { 11 | t.false(isCapital("hello")); 12 | t.false(isCapital("hELLO")); 13 | t.false(isCapital("hellO")); 14 | }); 15 | 16 | test("capital() returns a capital string", t => { 17 | t.is(capital("hello"), "Hello"); 18 | t.is(capital("Hello"), "Hello"); 19 | t.is(capital("hellO"), "HellO"); 20 | t.is(capital("hELLO"), "HELLO"); 21 | }); 22 | 23 | test("capital() ignores non letters", t => { 24 | t.is(capital("_hello"), "_hello"); 25 | t.is(capital("0hello"), "0hello"); 26 | }); 27 | -------------------------------------------------------------------------------- /web/docs/src/gatsby-plugin-theme-ui/headings.js: -------------------------------------------------------------------------------- 1 | const headingBase = { 2 | fontFamily: 'heading', 3 | lineHeight: 'heading', 4 | fontWeight: 'heading', 5 | mt: 0, 6 | mb: 3, 7 | '::before': { 8 | content: '" "', 9 | display: 'block', 10 | paddingTop: 30, 11 | marginBottom: 40, 12 | borderBottom: '1px solid', 13 | borderBottomColor: 'borderColor' 14 | } 15 | }; 16 | 17 | export default { 18 | h1: { 19 | ...headingBase, 20 | fontSize: 5 21 | }, 22 | h2: { 23 | ...headingBase, 24 | fontSize: 4 25 | }, 26 | h3: { 27 | ...headingBase, 28 | fontSize: 3 29 | }, 30 | h4: { 31 | ...headingBase, 32 | fontSize: 2 33 | }, 34 | h5: { 35 | ...headingBase, 36 | fontSize: 1 37 | }, 38 | h6: { 39 | ...headingBase, 40 | fontSize: 0 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /web/docs/src/gatsby-plugin-theme-ui/colors.js: -------------------------------------------------------------------------------- 1 | export default { 2 | text: "#000", 3 | background: "#fff", 4 | primary: "#e63b19", 5 | secondary: "#c70d3a", 6 | sidebar: "#eee", 7 | borderColor: "rgba(0, 0, 0, 0.15)", 8 | modes: { 9 | dark: { 10 | text: "#fff", 11 | background: "#222", 12 | primary: "#f638dc", 13 | secondary: "#ff7976", 14 | sidebar: "#111", 15 | borderColor: "rgba(255, 255, 255, 0.15)" 16 | }, 17 | cool: { 18 | text: "#fff", 19 | background: "#05386b", 20 | primary: "#5cdb95", 21 | secondary: "#bef992", 22 | sidebar: "#052e56", 23 | borderColor: "rgba(255, 255, 255, 0.15)" 24 | }, 25 | deep: { 26 | text: "#fff", 27 | background: "hsl(230,25%,18%)", 28 | primary: "hsl(260, 100%, 80%)", 29 | secondary: "hsl(290, 100%, 80%)", 30 | sidebar: "hsla(230, 20%, 0%, 20%)", 31 | borderColor: "rgba(255, 255, 255, 0.15)" 32 | } 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /packages/utils/src/misc.ts: -------------------------------------------------------------------------------- 1 | import {ItemComparison} from "./types"; 2 | import {ID, ID_SUFFIX} from "./constants"; 3 | 4 | /* istanbul ignore next */ 5 | export function isRelated(id: number | string, name: string): ItemComparison { 6 | return item => `${item[name]}` === `${id}`; 7 | } 8 | 9 | /* istanbul ignore next */ 10 | export function isSame(id: string): ItemComparison { 11 | return isRelated(id, "id"); 12 | } 13 | 14 | export function isId(str: string): boolean { 15 | return str === ID; 16 | } 17 | 18 | export function isRelative(str: string): boolean { 19 | return str.endsWith(ID_SUFFIX); 20 | } 21 | 22 | export function withRequired(condition: boolean): string { 23 | return condition ? "!" : ""; 24 | } 25 | 26 | export function withArray(str: string, condition: boolean): string { 27 | return condition ? `[${str}!]` : str; 28 | } 29 | 30 | export function withoutArray(str: string): string { 31 | return str.replace(/\[(.*)!]/, "$1"); 32 | } 33 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change. 4 | 5 | Please note we have a code of conduct, please follow it in all your interactions with the project. 6 | 7 | ## Pull Request Process 8 | 9 | Ensure any install or build dependencies are removed before the end of the layer when doing a build. 10 | Fork the repository and create a new branch (feature/my-feature) 11 | Commit changes following the "conventional-changelog" rules. 12 | Do not modify any versions manually. Don't build new versions. 13 | Use the PULL_REQUEST_TEMPLATE 14 | 15 | ## Reporting issues 16 | 17 | Ensure any install or build dependencies are removed before the end of the layer when doing a build. 18 | Create a new issue (bug/some-bug) 19 | Always list "yarn version", "node version", "npm version" and "stickyroll version" 20 | Use the ISSUE_TEMPLATE 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /packages/graphql/src/utils/type.ts: -------------------------------------------------------------------------------- 1 | import { isFloat, isBoolean, isNumber, isDate, isPlainObject, isString } from "@phony/utils"; 2 | 3 | export const types: {[key: string]: string} = { 4 | id: "ID", 5 | date: "String", 6 | string: "String", 7 | float: "Float", 8 | integer: "Int", 9 | boolean: "Boolean", 10 | object: "JSON" 11 | }; 12 | 13 | export function getType(value: any): string { 14 | if (isDate(value)) { 15 | return "date"; 16 | } 17 | if (isNumber(value)) { 18 | return isFloat(value) ? "float" : "integer"; 19 | } 20 | if (isString(value)) { 21 | return "string"; 22 | } 23 | if (isBoolean(value)) { 24 | return "boolean"; 25 | } 26 | /* istanbul ignore else */ 27 | if (isPlainObject(value)) { 28 | return "object"; 29 | } 30 | } 31 | export function getSchemaType(value: any): string { 32 | return types[getType(value)]; 33 | } 34 | 35 | export function buildTypeDefStr(type: string, name: string, defs: string[]): string { 36 | return `${type} ${name} {\n ${defs.join("\n ").trim()}\n}`; 37 | } 38 | -------------------------------------------------------------------------------- /packages/utils/test/number.ts: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import {isFloat, isInteger} from "../src/number"; 3 | 4 | test("isFloat() returns `true` for floats", t => { 5 | t.true(isFloat(0.1)); 6 | t.true(isFloat(0.234)); 7 | t.true(isFloat(1.2)); 8 | t.true(isFloat(-1.2)); 9 | }); 10 | 11 | test("isFloat() returns `false` for integers", t => { 12 | t.false(isFloat(0)); 13 | t.false(isFloat(234)); 14 | t.false(isFloat(1)); 15 | t.false(isFloat(1.0)); 16 | t.false(isFloat(0.0)); 17 | t.false(isFloat(-1)); 18 | t.false(isFloat(-1.0)); 19 | }); 20 | 21 | 22 | test("isInteger() returns `true` for integers", t => { 23 | t.true(isInteger(0)); 24 | t.true(isInteger(234)); 25 | t.true(isInteger(1)); 26 | t.true(isInteger(1.0)); 27 | t.true(isInteger(0.0)); 28 | t.true(isInteger(-1)); 29 | t.true(isInteger(-1.0)); 30 | }); 31 | 32 | test("isFloat() returns `false` for floats", t => { 33 | t.false(isInteger(0.1)); 34 | t.false(isInteger(0.234)); 35 | t.false(isInteger(1.2)); 36 | t.false(isInteger(-1.2)); 37 | }); 38 | -------------------------------------------------------------------------------- /examples/apollo/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import {AppContextType, AppType} from "next/dist/next-server/lib/utils"; 2 | import * as React from "react"; 3 | import {ApolloProvider} from "@apollo/client"; 4 | import {ThemeProvider} from "@material-ui/styles"; 5 | import client from "../utils/apollo"; 6 | import {appWithTranslation} from "../utils/i18n"; 7 | import {theme} from "../theme"; 8 | import "../empty.css"; 9 | 10 | const Cockpit: AppType = ({Component, pageProps}) => { 11 | return ( 12 | 13 | 14 | 15 | 16 | 17 | ); 18 | }; 19 | 20 | async function getPageProps({Component, ctx}: AppContextType) { 21 | return Component.getInitialProps ? Component.getInitialProps(ctx) : {}; 22 | } 23 | 24 | Cockpit.getInitialProps = async (appContext: AppContextType) => { 25 | return { 26 | pageProps: { 27 | ...(await getPageProps(appContext)) 28 | } 29 | }; 30 | }; 31 | 32 | export default appWithTranslation(Cockpit); 33 | -------------------------------------------------------------------------------- /packages/graphql/src/utils/query.ts: -------------------------------------------------------------------------------- 1 | import { getNameVariants } from "./name"; 2 | import { isCapital } from "@phony/utils"; 3 | import omit from "lodash.omit"; 4 | import { Database, NameMap } from "./types"; 5 | import { buildFilter } from "./filter"; 6 | import { buildTypeDefStr } from "./type"; 7 | 8 | export function buildQueryDefs(data: Database, typeNames: NameMap, phonyInputs: string[]) { 9 | const queryDefs = []; 10 | Object.entries(data).forEach(([key, collection]) => { 11 | const names = getNameVariants(key); 12 | const localNames = typeNames[key]; 13 | const [first] = collection; 14 | const removals = Object.keys(first).filter(isCapital); 15 | buildFilter(omit(first, removals), localNames, phonyInputs); 16 | queryDefs.push( 17 | `${localNames.get.all}(pagination: Pagination, sorting: Sorting, filter: ${localNames.input.filter}): [${names.singular.capital}]!` 18 | ); 19 | queryDefs.push(`${localNames.get.byId}(id: ID!): ${names.singular.capital}`); 20 | queryDefs.push(`${localNames.get.meta}: MetaData!\n`); 21 | }); 22 | return buildTypeDefStr("type", "Query", queryDefs); 23 | } 24 | -------------------------------------------------------------------------------- /packages/graphql/test/utils/type.ts: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import {types, getType, getSchemaType, buildTypeDefStr} from "../../src/utils/type"; 3 | 4 | test("getType() should determine the type of an datatype", t => { 5 | t.is(getType(""), "string"); 6 | t.is(getType("a"), "string"); 7 | t.is(getType(1), "integer"); 8 | t.is(getType(0.1), "float"); 9 | t.is(getType(false), "boolean"); 10 | t.is(getType(new Date()), "date"); 11 | t.is(getType({a: "A"}), "object"); 12 | }); 13 | 14 | test("getSchemaType() should declare the type of an datatype", t => { 15 | t.is(getSchemaType(""), types.string); 16 | t.is(getSchemaType("a"), types.string); 17 | t.is(getSchemaType(1), types.integer); 18 | t.is(getSchemaType(0.1), types.float); 19 | t.is(getSchemaType(false), types.boolean); 20 | t.is(getSchemaType(new Date()), types.date); 21 | t.is(getSchemaType({a: "A"}), types.object); 22 | }); 23 | 24 | const TYPEDEF_STR = `type Foo { 25 | foo: String 26 | }`; 27 | test("buildTypeDefStr() should build a graphql typedef string", t => { 28 | t.is(buildTypeDefStr("type", "Foo", ["foo: String"]), TYPEDEF_STR); 29 | }); 30 | -------------------------------------------------------------------------------- /packages/graphql/src/utils/types.ts: -------------------------------------------------------------------------------- 1 | export type RootFunction = (...args: any[]) => any; 2 | 3 | export interface Root { 4 | [key: string]: RootFunction; 5 | } 6 | 7 | export interface DatabaseEntry { 8 | id: string | number; 9 | [key: string]: any; 10 | } 11 | 12 | export type DatabaseCollection = DatabaseEntry[]; 13 | 14 | export interface Entry { 15 | [key: string]: any; 16 | } 17 | export interface Database { 18 | [key: string]: DatabaseCollection; 19 | } 20 | 21 | export interface NameConfig { 22 | get: { 23 | all: string; 24 | byId: string; 25 | meta: string; 26 | }; 27 | post: { 28 | create: string; 29 | update: string; 30 | delete: string; 31 | }; 32 | input: { 33 | filterFields: string; 34 | filter: string; 35 | create: string; 36 | update: string; 37 | }, 38 | internalFields: { 39 | [key: string]: string; 40 | } 41 | ; 42 | } 43 | 44 | export interface NameMap { 45 | [key: string]: NameConfig; 46 | } 47 | 48 | 49 | export interface PonyConfig { 50 | queryConfig: NameConfig; 51 | schema: string; 52 | input: string; 53 | database: string; 54 | port: string | number; 55 | } 56 | -------------------------------------------------------------------------------- /examples/apollo/components/table.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import MaterialTable, {Icons} from "material-table"; 3 | import {tableIcons} from "./icons"; 4 | import {useTranslation} from 'react-i18next' 5 | 6 | export default function Table({options, columns, data, ...props}) { 7 | const {t} = useTranslation("translation"); 8 | return ( 9 | 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Gregor Adams 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@phony/server", 3 | "version": "1.0.1", 4 | "description": "A simple mock-server with cors enabled", 5 | "repository": "git@github.com:pixelass/phony.git", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Gregor Adams", 9 | "email": "greg@pixelass.com", 10 | "url": "https://pixelass.com" 11 | }, 12 | "keywords": [ 13 | "graphql", 14 | "graphiql", 15 | "mocking", 16 | "mock", 17 | "server", 18 | "express", 19 | "utils", 20 | "toolchain" 21 | ], 22 | "main": "index.js", 23 | "module": "dist/esm/index.js", 24 | "typings": "dist/index.d.ts", 25 | "directories": { 26 | "lib": "dist", 27 | "doc": "doc", 28 | "test": "test" 29 | }, 30 | "files": [ 31 | "dist" 32 | ], 33 | "scripts": { 34 | "prebuild": "rimraf dist", 35 | "build": "rollup -c", 36 | "watch": "rollup -cw" 37 | }, 38 | "dependencies": { 39 | "@types/cors": "^2.8.6", 40 | "@types/express": "^4.17.3", 41 | "cors": "^2.8.5", 42 | "express": "^4.17.1" 43 | }, 44 | "publishConfig": { 45 | "access": "public" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/server/rollup.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const babel = require("rollup-plugin-babel"); 3 | const json = require("@rollup/plugin-json"); 4 | const commonjs = require("@rollup/plugin-commonjs"); 5 | const typescript = require("rollup-plugin-typescript2"); 6 | const createBanner = require("../../utils/rollup/banner"); 7 | 8 | module.exports = function() { 9 | const cwd = process.cwd(); 10 | const pkg = require(path.resolve(cwd, "package.json")); 11 | const tsconfig = path.resolve(cwd, "tsconfig.json"); 12 | return [ 13 | { 14 | input: "src/index.ts", 15 | external: [...Object.keys(pkg.dependencies || {})], 16 | output: [ 17 | { 18 | banner: createBanner(pkg), 19 | file: `dist/${pkg.main}`, 20 | format: "cjs" 21 | }, 22 | { 23 | banner: createBanner(pkg), 24 | file: pkg.module, 25 | format: "esm" 26 | } 27 | ], 28 | plugins: [ 29 | commonjs(), 30 | json(), 31 | babel(), 32 | typescript({ 33 | tsconfig, 34 | tsconfigOverride: { 35 | compilerOptions: { 36 | module: "es6" 37 | } 38 | } 39 | }) 40 | ] 41 | } 42 | ]; 43 | }; 44 | -------------------------------------------------------------------------------- /packages/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@phony/cli", 3 | "version": "1.0.1", 4 | "description": "A simple mock-server with cors enabled", 5 | "repository": "git@github.com:pixelass/phony.git", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Gregor Adams", 9 | "email": "greg@pixelass.com", 10 | "url": "https://pixelass.com" 11 | }, 12 | "keywords": [ 13 | "graphql", 14 | "graphiql", 15 | "mocking", 16 | "mock", 17 | "server", 18 | "express", 19 | "utils", 20 | "toolchain", 21 | "cli" 22 | ], 23 | "bin": { 24 | "phonyql": "phonyql.js" 25 | }, 26 | "directories": { 27 | "lib": "dist", 28 | "doc": "doc", 29 | "test": "test" 30 | }, 31 | "files": [ 32 | "dist" 33 | ], 34 | "scripts": { 35 | "prebuild": "rimraf dist", 36 | "build": "rollup -c", 37 | "watch": "rollup -cw" 38 | }, 39 | "dependencies": { 40 | "cosmiconfig": "^6.0.0", 41 | "meow": "^6.0.1" 42 | }, 43 | "devDependencies": { 44 | "@phony/graphql": "^1.0.1" 45 | }, 46 | "peerDependencies": { 47 | "@phony/graphql": ">=0.1.0" 48 | }, 49 | "publishConfig": { 50 | "access": "public" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/apollo/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import Document from "next/document"; 3 | import MaterialUiServerStyleSheet from "@material-ui/styles/ServerStyleSheets"; 4 | import client from "../utils/apollo"; 5 | import {APOLLO_STATE} from "../constants/root"; 6 | 7 | export default class CustomDocument extends Document { 8 | static async getInitialProps(ctx) { 9 | const materialUiSheets = new MaterialUiServerStyleSheet(); 10 | const originalRenderPage = ctx.renderPage; 11 | try { 12 | ctx.renderPage = () => 13 | originalRenderPage({ 14 | enhanceApp: App => props => 15 | materialUiSheets.collect() 16 | }); 17 | 18 | const initialProps = await Document.getInitialProps(ctx); 19 | return { 20 | ...initialProps, 21 | styles: ( 22 | 23 | {initialProps.styles} 24 | {materialUiSheets.getStyleElement()} 25 |