├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
├── manifest.json
└── thumb.jpg
└── src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── json-to-simple-graphql-schema
├── index.js
├── remove-duplicate-types.js
├── stringify-schema.js
└── to-schema.js
├── logo.svg
└── serviceWorker.js
/.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 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # json-to-graphql-schema-online
2 |
3 | Transforms JSON input into a GraphQL schema.
4 |
5 | URL: [https://matheusr42.github.io/json-to-graphql-schema-online/](https://matheusr42.github.io/json-to-graphql-schema-online/)
6 |
7 | ### Why would I use this?
8 |
9 | Let's say you want to use an existing REST API in a GraphQL service and expose the data that API provides. You'll need to expose that data in a GraphQL schema. Without this tool, you'll have to slog through the JSON response and manually extract and convert the relevant types to GraphQL schema types. This tool attempts to provide an automated reasonable first-pass at that effort.
10 |
11 | This app is based on [json-to-simple-graphql-schema](https://github.com/walmartlabs/json-to-simple-graphql-schema) from walmartlabs. [@maxnachlinger](https://github.com/maxnachlinger) done the real work I just implement the web interface :)
12 |
13 |
14 | When I developed this app there was only the command line interface, now [@maxnachlinger](https://github.com/maxnachlinger) created a web official version. I recommend to see his version, since it is importing the oficial lib.
15 | https://github.com/walmartlabs/json-to-simple-graphql-schema
16 |
17 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "json-to-graphql-schema-online",
3 | "version": "0.1.0",
4 | "private": true,
5 | "homepage": "http://matheusr42.github.io/json-to-graphql-schema-online",
6 | "dependencies": {
7 | "lodash": "^4.17.11",
8 | "pascal-case": "^2.0.1",
9 | "react": "^16.8.6",
10 | "react-codemirror": "^1.0.0",
11 | "react-codemirror2": "^5.1.0",
12 | "react-dom": "^16.8.6",
13 | "react-scripts": "2.1.8",
14 | "react-split-pane": "^0.1.87",
15 | "sweetalert2": "^8.8.0",
16 | "util": "^0.11.1"
17 | },
18 | "scripts": {
19 | "start": "react-scripts start",
20 | "build": "react-scripts build",
21 | "test": "react-scripts test",
22 | "eject": "react-scripts eject",
23 | "predeploy": "npm run build",
24 | "deploy": "gh-pages -d build"
25 | },
26 | "eslintConfig": {
27 | "extends": "react-app"
28 | },
29 | "browserslist": [
30 | ">0.2%",
31 | "not dead",
32 | "not ie <= 11",
33 | "not op_mini all"
34 | ],
35 | "devDependencies": {
36 | "gh-pages": "^2.0.1"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MatheusR42/json-to-graphql-schema-online/59f34b585f466446940d509d36275bd5fdb4b6d9/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
15 |
16 |
25 | Convert JSON to GraphQL Schema Online
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/public/thumb.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MatheusR42/json-to-graphql-schema-online/59f34b585f466446940d509d36275bd5fdb4b6d9/public/thumb.jpg
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .Resizer {
2 | background: #000;
3 | opacity: .2;
4 | z-index: 1;
5 | -moz-box-sizing: border-box;
6 | -webkit-box-sizing: border-box;
7 | box-sizing: border-box;
8 | -moz-background-clip: padding;
9 | -webkit-background-clip: padding;
10 | background-clip: padding-box;
11 | }
12 |
13 | .Resizer:hover {
14 | -webkit-transition: all 2s ease;
15 | transition: all 2s ease;
16 | }
17 |
18 | .Resizer.horizontal {
19 | height: 11px;
20 | margin: -5px 0;
21 | border-top: 5px solid rgba(255, 255, 255, 0);
22 | border-bottom: 5px solid rgba(255, 255, 255, 0);
23 | cursor: row-resize;
24 | width: 100%;
25 | }
26 |
27 | .Resizer.horizontal:hover {
28 | border-top: 5px solid rgba(0, 0, 0, 0.5);
29 | border-bottom: 5px solid rgba(0, 0, 0, 0.5);
30 | }
31 |
32 | .Resizer.vertical {
33 | width: 11px;
34 | margin: 0 -5px;
35 | border-left: 5px solid rgba(255, 255, 255, 0);
36 | border-right: 5px solid rgba(255, 255, 255, 0);
37 | cursor: col-resize;
38 | }
39 |
40 | .Resizer.vertical:hover {
41 | border-left: 5px solid rgba(0, 0, 0, 0.5);
42 | border-right: 5px solid rgba(0, 0, 0, 0.5);
43 | }
44 | .Resizer.disabled {
45 | cursor: not-allowed;
46 | }
47 | .Resizer.disabled:hover {
48 | border-color: transparent;
49 | }
50 |
51 | .CodeMirror {
52 | /* Set height, width, borders, and global font properties here */
53 | font-family: monospace;
54 | height: calc(100vh - 10px);
55 | color: black;
56 | }
57 |
58 | h1 {
59 | padding: 0;
60 | margin: 0;
61 | }
62 |
63 | h2 {
64 | border-bottom: 1px solid #000;
65 | padding: 10px;
66 | margin: 0;
67 | }
68 |
69 | header {
70 | text-align: center;
71 | border-bottom: 1px solid #000;
72 | padding: 20px;
73 | }
74 |
75 |
76 | button {
77 | font-size: 24px;
78 | text-transform: uppercase;
79 | margin-top: 20px;
80 | }
81 |
82 | .source-code {
83 | position: fixed;
84 | bottom: 10px;
85 | right: 10px;
86 | z-index: 99;
87 | }
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Swal from 'sweetalert2'
3 | import SplitPane from 'react-split-pane';
4 | import {Controlled as CodeMirror} from 'react-codemirror2';
5 |
6 | import {
7 | jsonToSchema
8 | } from './json-to-simple-graphql-schema';
9 |
10 | import 'codemirror/lib/codemirror.css';
11 | import './App.css';
12 |
13 | class App extends Component {
14 | constructor(props) {
15 | super(props);
16 |
17 | this.state = {
18 | jsonInput:
19 | `{
20 | "userId": 1,
21 | "id": 1,
22 | "addresses": [
23 | {
24 | "id": 2,
25 | "name": "two"
26 | },
27 | {
28 | "id": 1,
29 | "name": "one"
30 | }
31 | ]
32 | }`
33 | ,
34 | schema:
35 | `type Addresses {
36 | id: Int
37 | name: String
38 | }
39 |
40 | type AutogeneratedMainType {
41 | userId: Int
42 | id: Int
43 | addresses: [Addresses]
44 | }`
45 | }
46 | }
47 |
48 | convert() {
49 | const { jsonInput } = this.state;
50 |
51 | const { error, value } = jsonToSchema({ jsonInput });
52 | if (error) {
53 | const { title, message } = error;
54 |
55 | Swal.fire({
56 | title,
57 | text: message,
58 | type: 'error'
59 | })
60 |
61 | return;
62 | }
63 |
64 | this.setState({ schema: value });
65 | }
66 |
67 | render() {
68 | const width = window.innerWidth / 2;
69 | const { jsonInput, schema } = this.state;
70 |
71 | return (
72 |
73 |
74 | Convert JSON to GraphQL Schema Online
75 |
76 |
77 |
78 |
79 |
80 |
81 |
JSON Input
82 |
83 | {
85 | this.setState({ jsonInput: value});
86 | }}
87 | onChange={(editor, data, value) => {
88 | }}
89 | value={jsonInput}
90 | options={{ mode: 'json', lineNumbers: true, tabSize: 2 }}
91 | />
92 |
93 |
94 |
GraphQL schema
95 | {
97 | this.setState({ schema: value});
98 | }}
99 | onChange={(editor, data, value) => {
100 | }}
101 | value={schema}
102 | options={{ lineNumbers: true, tabSize: 2 }}
103 | />
104 |
105 |
106 |
107 |
112 | Source code
113 |
114 |
115 | );
116 | }
117 | }
118 |
119 | export default App;
120 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
6 | sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/src/json-to-simple-graphql-schema/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) [2018]-present, Walmart Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License."
15 | */
16 |
17 | "use strict";
18 |
19 | import toSchema from "./to-schema";
20 | import stringifySchema from "./stringify-schema";
21 |
22 | const safeFnExecute = fn => {
23 | try {
24 | return { value: fn() };
25 | } catch (error) {
26 | return { error };
27 | }
28 | };
29 |
30 | const validateJson = jsonInput => {
31 | const { error: jsonError, value } = safeFnExecute(() => JSON.parse(jsonInput));
32 |
33 | if (jsonError) {
34 | return {
35 | error: {
36 | title: 'Invalid JSON received',
37 | message: jsonError.message
38 | }
39 | };
40 | }
41 |
42 | if (!value) {
43 | return {
44 | error: {
45 | title: 'Invalid JSON received',
46 | message: null
47 | }
48 | };
49 | }
50 |
51 | return { value };
52 | };
53 |
54 | const jsonToSchema = ({ baseType = "AutogeneratedMainType", jsonInput }) => {
55 | const { error, value } = validateJson(jsonInput);
56 | if (error) {
57 | return { error };
58 | }
59 |
60 | return safeFnExecute(() => stringifySchema(baseType, toSchema(value)));
61 | };
62 |
63 | export {
64 | jsonToSchema,
65 | validateJson
66 | };
67 |
--------------------------------------------------------------------------------
/src/json-to-simple-graphql-schema/remove-duplicate-types.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) [2018]-present, Walmart Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License."
15 | */
16 |
17 | "use strict";
18 |
19 | const combineTypes = str =>
20 | str
21 | .split("type ")
22 | // clear empty lines
23 | .filter(i => i)
24 | .reduce((accum, fragment, idx) => {
25 | const full = `type ${fragment}`.trim();
26 |
27 | const fragmentArr = fragment
28 | .trim()
29 | .split(" ")
30 | .map(s => s.trim());
31 | const name = fragmentArr.shift();
32 | const amtFields = fragmentArr.filter(s => s.indexOf(":") > -1).length;
33 |
34 | // keep the type with the most fields
35 | if (!accum[name] || accum[name].amtFields < amtFields) {
36 | accum[name] = { full, name, amtFields, idx };
37 | }
38 |
39 | return accum;
40 | }, {});
41 |
42 | const extractPossibleIdenticalTypes = str =>
43 | str
44 | .split("type ")
45 | // clear empty lines
46 | .filter(i => i)
47 | .reduce((accum, fragment) => {
48 | const fragmentArr = fragment
49 | .trim()
50 | .split(" ")
51 | .map(s => s.trim());
52 | const name = fragmentArr.shift();
53 | const fieldsSignature = fragmentArr.join(" ");
54 |
55 | if (!accum[fieldsSignature]) {
56 | accum[fieldsSignature] = [name];
57 | return accum;
58 | }
59 |
60 | if (accum[fieldsSignature].indexOf(name) === -1) {
61 | accum[fieldsSignature].push(name);
62 | }
63 | return accum;
64 | }, {});
65 |
66 | const removeDuplicateTypes = str => {
67 | const schemaStr = Object.values(combineTypes(str))
68 | .sort((a0, a1) => a0.idx - a1.idx)
69 | .map(({ full }) => full)
70 | .join("\n\n");
71 |
72 | const probablyDuplicates = Object.values(extractPossibleIdenticalTypes(schemaStr))
73 | .filter(arr => arr.length > 1)
74 | .map(arr => `# ${arr.join(" ")}`)
75 | .join("\n");
76 |
77 | return `${schemaStr}${
78 | probablyDuplicates ? `\n\n# Types with identical fields:\n${probablyDuplicates}` : ""
79 | }`;
80 | };
81 |
82 | export default removeDuplicateTypes;
83 |
--------------------------------------------------------------------------------
/src/json-to-simple-graphql-schema/stringify-schema.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) [2018]-present, Walmart Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License."
15 | */
16 |
17 | "use strict";
18 |
19 | // import util from "util";
20 | import _ from "lodash";
21 | import pascalCase from "pascal-case";
22 | import removeDuplicateTypes from "./remove-duplicate-types";
23 |
24 | const schemaToString = (typeName, obj) => {
25 | let str = "";
26 |
27 | Object.keys(obj).forEach(key => {
28 | if (_.isArray(obj[key])) {
29 | const firstElement = obj[key][0];
30 |
31 | if (_.isArray(firstElement) || _.isObject(firstElement)) {
32 | const newTypeName = pascalCase(key);
33 | str += schemaToString(newTypeName, firstElement);
34 | obj[key][0] = newTypeName;
35 | }
36 | return;
37 | }
38 |
39 | if (_.isObject(obj[key])) {
40 | const newTypeName = pascalCase(key);
41 | str += schemaToString(newTypeName, obj[key]);
42 | obj[key] = newTypeName;
43 | }
44 | });
45 |
46 | //original implementation to node
47 | // const newObjString = util.inspect(obj, { depth: null, compact: false });
48 |
49 | // return `${str}type ${typeName} ${newObjString.replace(/'/g, "")} `
50 | // .replace(/\[\n/g, "[")
51 | // .replace(/\[\s+/g, "[")
52 | // .replace(/\n\s+\]/g, "]")
53 | // .replace(/,/g, "")
54 | // .replace(/ {3,}/g, " ");
55 |
56 | //adaptation to web
57 | const newObjString = JSON.stringify(obj, null, 4);
58 |
59 | return `${str}type ${typeName} ${newObjString.replace(/"/g, "")} `
60 | .replace(/\[\n/g, "[")
61 | .replace(/\[\s+/g, "[")
62 | .replace(/\n\s+\]/g, "]")
63 | .replace(/,/g, "")
64 | .replace(/ {3,}/g, " ");
65 | };
66 |
67 | const stringifySchema = _.flow([schemaToString, removeDuplicateTypes]);
68 |
69 | export default stringifySchema;
70 |
71 |
--------------------------------------------------------------------------------
/src/json-to-simple-graphql-schema/to-schema.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) [2018]-present, Walmart Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License."
15 | */
16 |
17 | "use strict";
18 |
19 | import _ from "lodash";
20 |
21 | const transformPrimitive = (key, value) => {
22 | if (_.isInteger(value)) {
23 | return "Int";
24 | }
25 | if (_.isBoolean(value)) {
26 | return "Boolean";
27 | }
28 | if (_.isNumber(value)) {
29 | return "Float";
30 | }
31 | return "String";
32 | };
33 |
34 | const toSchema = input => {
35 | const result = {};
36 | const processedItemsCache = [];
37 | const stack = [{ obj: input, path: "" }];
38 |
39 | while (stack.length > 0) {
40 | const { obj, path } = stack.pop();
41 |
42 | Object.keys(obj).forEach(key => {
43 | let currentValue = obj[key];
44 |
45 | if (!_.isArray(currentValue) && !_.isObject(currentValue)) {
46 | const newObjValue = transformPrimitive(key, currentValue);
47 | const newObjValuePath = path ? `${path}.` : "";
48 | _.set(result, `${newObjValuePath}${key}`, newObjValue);
49 | return;
50 | }
51 |
52 | // all this to guard against circular refs
53 | if (_.some(processedItemsCache, o => o === currentValue)) {
54 | return;
55 | }
56 |
57 | processedItemsCache.push(currentValue);
58 |
59 | const pathPrefix = path ? `${path}.` : "";
60 | const newPath = `${pathPrefix}${key}`;
61 |
62 | // only use the first element in the array since we're assuming the same type for all
63 | // array elements
64 | if (_.isArray(currentValue)) {
65 | currentValue = [currentValue[0]];
66 | }
67 |
68 | stack.push({
69 | obj: currentValue,
70 | path: newPath
71 | });
72 | });
73 | }
74 |
75 | processedItemsCache.splice(0, processedItemsCache.length);
76 | return result;
77 | };
78 |
79 | export default toSchema;
80 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------