@thoughtbot/superglue 0.53.3 → 0.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,53 +1,91 @@
1
1
  {
2
2
  "name": "@thoughtbot/superglue",
3
- "version": "0.53.3",
3
+ "version": "0.54.0",
4
4
  "description": "Use a vanilla Rails with React and Redux",
5
+ "scripts": {
6
+ "build": "tsup",
7
+ "dev": "tsup --watch",
8
+ "clean": "rm -rf dist",
9
+ "lint": "run-p lint:eslint lint:types lint:prettier",
10
+ "lint:eslint": "eslint --max-warnings=0 --ext js,jsx,ts,tsx ./lib",
11
+ "lint:prettier": "prettier --check '**/*' --ignore-unknown",
12
+ "lint:types": "tsc",
13
+ "fix:prettier": "prettier --write '**/*' --ignore-unknown",
14
+ "test": "vitest",
15
+ "test:run": "vitest run",
16
+ "test:cov": "vitest --coverage",
17
+ "test:all": "npm lint && npm test:run",
18
+ "pub:beta": "npm build && npm publish --tag beta",
19
+ "pub:release": "npm build && npm publish"
20
+ },
5
21
  "repository": {
6
22
  "type": "git",
7
23
  "url": "git+https://github.com/thoughtbot/superglue.git"
8
24
  },
9
25
  "author": "Johny Ho",
26
+ "main": "dist/cjs/superglue.cjs",
27
+ "module": "dist/superglue.mjs",
28
+ "types": "dist/superglue.d.mts",
29
+ "exports": {
30
+ "./package.json": "./package.json",
31
+ ".": {
32
+ "types": "./dist/superglue.d.mts",
33
+ "import": "./dist/superglue.mjs",
34
+ "default": "./dist/cjs/superglue.cjs"
35
+ },
36
+ "./action_creators": {
37
+ "types": "./dist/action_creators.d.mts",
38
+ "import": "./dist/action_creators.mjs",
39
+ "default": "./dist/cjs/action_creators.cjs"
40
+ }
41
+ },
10
42
  "license": "MIT",
11
43
  "bugs": {
12
44
  "url": "https://github.com/thoughtbot/superglue/issues"
13
45
  },
14
46
  "homepage": "https://github.com/thoughtbot/superglue#readme",
15
47
  "devDependencies": {
16
- "@babel/cli": "^7.14.3",
17
- "@babel/core": "^7.14.3",
18
- "@babel/preset-env": "^7.14.4",
19
- "@babel/preset-react": "^7.13.13",
20
- "core-js": "^2.6.12",
21
- "enzyme": "^3.11.0",
22
- "enzyme-adapter-react-16": "^1.15.6",
23
- "eslint": "^7.28.0",
24
- "eslint-config-prettier": "^8.3.0",
25
- "eslint-plugin-prettier": "^3.4.0",
26
- "eslint-plugin-react": "^7.24.0",
48
+ "@reduxjs/toolkit": "^2.2.5",
49
+ "@testing-library/dom": "^10.4.0",
50
+ "@testing-library/jest-dom": "^6.5.0",
51
+ "@testing-library/react": "^16.0.1",
52
+ "@testing-library/user-event": "^14.5.2",
53
+ "@types/url-parse": "^1.4.11",
54
+ "@typescript-eslint/eslint-plugin": "^7.15.0",
55
+ "@typescript-eslint/parser": "^7.15.0",
56
+ "@vitejs/plugin-react": "^4.3.1",
57
+ "@vitest/coverage-v8": "^2.0.2",
58
+ "abortcontroller-polyfill": "^1.7.3",
59
+ "eslint": "^8.57.0",
60
+ "eslint-plugin-react": "^7.34.3",
27
61
  "fetch-headers": "^2.0.0",
28
62
  "fetch-mock": "^9.11.0",
29
- "history": "^5.3.0",
30
- "jest": "^27.0.4",
63
+ "jsdom": "^24.1.0",
31
64
  "node-fetch": "^2.6.1",
65
+ "npm-run-all": "^4.1.5",
32
66
  "prettier": "^2.3.1",
33
- "prop-types": "^15.7.2",
34
- "react": "^16.4.0",
35
- "react-dom": "^16.4.0",
67
+ "prettier-eslint": "^16.3.0",
68
+ "react": "^18.3.1",
69
+ "react-dom": "^18.3.1",
36
70
  "react-redux": "^7.2.4",
37
- "redux": "^4.1.0",
71
+ "redux": "^5.0.1",
38
72
  "redux-mock-store": "^1.5.4",
39
- "redux-thunk": "^2.3.0"
73
+ "redux-thunk": "^3.1.0",
74
+ "tsup": "^8.1.0",
75
+ "typedoc": "^0.26.5",
76
+ "typedoc-plugin-markdown": "^4.2.3",
77
+ "typedoc-plugin-missing-exports": "^3.0.0",
78
+ "typescript": "^5.5.3",
79
+ "vitest": "^2.0.2"
40
80
  },
41
81
  "peerDependencies": {
42
- "history": "^5.3.0",
43
82
  "react": ">=16",
44
83
  "react-redux": ">=7.2",
45
84
  "redux": ">=4.1",
46
85
  "redux-thunk": ">=2.3"
47
86
  },
48
87
  "dependencies": {
49
- "abortcontroller-polyfill": "^1.7.3",
50
- "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
88
+ "history": "^5.3.0",
51
89
  "url-parse": "^1.5.1"
52
90
  }
53
91
  }
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "removeComments": true,
4
+ "moduleResolution": "Node",
5
+ "strict": true,
6
+ "strictPropertyInitialization": false,
7
+ "jsx": "react",
8
+ "allowJs": false,
9
+ "target": "ES2021",
10
+ "allowSyntheticDefaultImports": true,
11
+ "esModuleInterop": true,
12
+ "noEmit": true
13
+ },
14
+ "include": ["./lib/**/*"]
15
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,29 @@
1
+ import type { Options } from 'tsup'
2
+ import { defineConfig } from 'tsup'
3
+
4
+ export default defineConfig((options) => {
5
+ const commonOptions: Partial<Options> = {
6
+ entry: {
7
+ superglue: 'lib/index.tsx',
8
+ action_creators: 'lib/action_creators/index.ts',
9
+ },
10
+ sourcemap: true,
11
+ ...options,
12
+ }
13
+
14
+ return [
15
+ {
16
+ ...commonOptions,
17
+ format: ['esm'],
18
+ outExtension: () => ({ js: '.mjs' }), // Add dts: '.d.ts' when egoist/tsup#1053 lands
19
+ dts: true,
20
+ clean: true,
21
+ },
22
+ {
23
+ ...commonOptions,
24
+ format: 'cjs',
25
+ outDir: './dist/cjs/',
26
+ outExtension: () => ({ js: '.cjs' }),
27
+ },
28
+ ]
29
+ })
package/typedoc.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "$schema": "https://typedoc-plugin-markdown.org/schema.json",
3
+ "plugin": ["typedoc-plugin-markdown"],
4
+ "entryPoints": [
5
+ "./lib/index.tsx",
6
+ "./lib/types/index.ts",
7
+ "./lib/types/requests.ts",
8
+ "./lib/components/Nav.tsx"
9
+ ],
10
+ "outputFileStrategy": "modules",
11
+ "flattenOutputFiles": true,
12
+ "readme": "none",
13
+ "expandParameters": true,
14
+ "expandObjects": true,
15
+ "hidePageTitle": true,
16
+ "sort": ["source-order", "external-last"],
17
+ "hidePageHeader": true,
18
+ "hideBreadcrumbs": true,
19
+ "indexFormat": "table",
20
+ "parametersFormat": "table",
21
+ "interfacePropertiesFormat": "table",
22
+ "classPropertiesFormat": "table",
23
+ "enumMembersFormat": "table",
24
+ "typeDeclarationFormat": "table",
25
+ "propertyMembersFormat": "table",
26
+ "excludeExternals": true,
27
+ "excludeInternal": true,
28
+ "useHTMLAnchors": true,
29
+ "out": "../docs/reference"
30
+ }
package/README.md DELETED
@@ -1,126 +0,0 @@
1
- <div align="center" style="padding: 30px 0px 20px 0px;">
2
- <img src="https://thoughtbot.github.io/superglue/images/superglue.svg" data-origin="images/superglue.svg" alt="Logo" width=250>
3
- </div>
4
-
5
- # Superglue
6
-
7
- Use classic Rails to build rich React Redux applications with **NO APIs** and
8
- **NO client-side routing**.
9
-
10
- [![Test superglue_js](https://github.com/thoughtbot/superglue/actions/workflows/build_js.yml/badge.svg)](https://github.com/thoughtbot/superglue/actions/workflows/build_js.yml)
11
- [![Test superglue_rails](https://github.com/thoughtbot/superglue/actions/workflows/build_rails.yml/badge.svg)](https://github.com/thoughtbot/superglue/actions/workflows/build_rails.yml)
12
-
13
- Superglue makes React and Redux as productive as Hotwire, Turbo and Stimulus.
14
- Its inspired by Turbolinks and designed to feel like a natural extension of
15
- Rails. Enjoy the benefits of Redux state management and React components
16
- without giving up the productivity of Rails form helpers, UJS, tag helpers,
17
- flash, cookie auth, and more.
18
-
19
- ## Caution
20
-
21
- This project is in its early phases of development. Its interface, behavior,
22
- and name are likely to change drastically before a major version release.
23
-
24
- ### No APIs
25
-
26
- Instead of APIs, Superglue leans on Rail's ability to respond to different
27
- [mime types](https://apidock.com/rails/ActionController/MimeResponds/InstanceMethods/respond_to)
28
- on the same route. In a Superglue application, if you direct your browser to
29
- `/dashboard.html`, you would see the HTML version of the content, and if you
30
- went to `/dashboard.json` you would see the JSON version of the exact same
31
- content down to the footer.
32
-
33
- The end result would be something like this:
34
-
35
- ![No Apis](https://thoughtbot.github.io/superglue/images/no_apis.png)
36
-
37
- ### Powered by Classic Rails
38
- Superglue leans on Rails. Features like the flash, cookie auth, and URL
39
- helpers continue to be useful. Here's a look at the directory structure of a
40
- typical Rails application with Superglue.
41
-
42
- ```treeview
43
- app/
44
- |-- controllers/
45
- |-- views/
46
- | |-- dashboard/
47
- | | |-- index.js # The React page component
48
- | | |-- index.json.props # The json for the page component
49
- | | |-- index.html.erb
50
- ```
51
-
52
- ### PropsTemplate
53
- Powering the JSON responses is PropsTemplate, a digable JSON templating DSL
54
- inspired by JBuilder. With PropsTemplate you can specify a path of the node you
55
- want, and PropsTemplate will walk the tree to it, skipping the execution of nodes
56
- that don't match the keypath.
57
-
58
- ![No Apis](https://thoughtbot.github.io/superglue/images/props_template.png)
59
-
60
- ### All together now!
61
- Superglue comes with batteries that bring all the above concepts together to make
62
- building popular SPA features easy, painless, and productive.
63
-
64
- #### SPA Navigation
65
- A popular ask of SPAs is page-to-page navigation without reloading. This is
66
- easily done with Superglue's own UJS attributes inspired by Turbolinks:
67
-
68
- ```jsx
69
- <a href='/posts' data-sg-visit />
70
- ```
71
-
72
- The above will request for `/posts` with an `accept` of `application/json`, and
73
- when the client receives the response, swap out the current component for the
74
- component the response asks for, and `pushState` on history.
75
-
76
-
77
- #### Easy Partial updates
78
- Some features rely on updating some parts of the existing page. Imagine
79
- implementing type-ahead search. In traditional applications, you may need a new
80
- controller, routes, a discussion over versioning, JSON serializer, plenty of
81
- new JS code, etc.
82
-
83
- ![haircuts](https://thoughtbot.github.io/superglue/images/haircuts.png)
84
-
85
- With Superglue, this can be done with a simple `onChange`
86
-
87
- ```js
88
- const onChange = (e) => (
89
- remote(`/dashboard?qry=${e.target.value}&props_at=data.header.search`)}
90
- )
91
- ```
92
-
93
- ?> `remote` and `visit` is a thunk [passed] to every page component.
94
-
95
- [passed]: ./navigation.md
96
-
97
- With `props_at`, the above will make a request to `/dashboard?qry=haircut`,
98
- dig your template for the `data.header.search` node, return it in the response,
99
- and immutably graft it in the exact same path on the redux store before finally
100
- letting React re-render.
101
-
102
- For more on what you can do, check out our documentation.
103
-
104
- #### Server-Side Rendering
105
- Server-Side Rendering is supported via [Humid](https://github.com/thoughtbot/humid).
106
- See the [documentation for server-side rendering][ssr docs].
107
-
108
- [ssr docs]: ./recipes/server-side-rendering.md
109
-
110
- ## Documentation
111
-
112
- Documentation is hosted on [Github pages](https://thoughtbot.github.io/superglue).
113
-
114
- ## Contributing
115
-
116
- Thank you, [contributors]!
117
-
118
- [contributors]: https://github.com/thoughtbot/superglue/graphs/contributors
119
-
120
- ## Special Thanks
121
-
122
- Thanks to [jbuilder](https://github.com/rails/jbuilder),
123
- [scour](https://github.com/rstacruz/scour),
124
- [turbolinks3](https://github.com/turbolinks/turbolinks-classic),
125
- [turbograft](https://github.com/Shopify/turbograft/),
126
- [turbostreamer](https://github.com/malomalo/turbostreamer)
@@ -1,160 +0,0 @@
1
- "use strict";
2
-
3
- exports.__esModule = true;
4
- var _exportNames = {
5
- copyPage: true,
6
- saveResponse: true,
7
- handleGraft: true,
8
- saveAndProcessPage: true
9
- };
10
- exports.copyPage = copyPage;
11
- exports.handleGraft = handleGraft;
12
- exports.saveAndProcessPage = saveAndProcessPage;
13
- exports.saveResponse = saveResponse;
14
-
15
- var _utils = require("../utils");
16
-
17
- var _urlParse = _interopRequireDefault(require("url-parse"));
18
-
19
- var _actions = require("../actions");
20
-
21
- var _requests = require("./requests");
22
-
23
- Object.keys(_requests).forEach(function (key) {
24
- if (key === "default" || key === "__esModule") return;
25
- if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
26
- if (key in exports && exports[key] === _requests[key]) return;
27
- exports[key] = _requests[key];
28
- });
29
-
30
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
31
-
32
- function copyPage(_ref) {
33
- var from = _ref.from,
34
- to = _ref.to;
35
- return {
36
- type: _actions.COPY_PAGE,
37
- payload: {
38
- from: from,
39
- to: to
40
- }
41
- };
42
- }
43
-
44
- function saveResponse(_ref2) {
45
- var pageKey = _ref2.pageKey,
46
- page = _ref2.page;
47
- pageKey = (0, _utils.urlToPageKey)(pageKey);
48
- return {
49
- type: _actions.SAVE_RESPONSE,
50
- payload: {
51
- pageKey: pageKey,
52
- page: page
53
- }
54
- };
55
- }
56
-
57
- function handleGraft(_ref3) {
58
- var pageKey = _ref3.pageKey,
59
- page = _ref3.page;
60
- pageKey = (0, _utils.urlToPageKey)(pageKey);
61
- return {
62
- type: _actions.HANDLE_GRAFT,
63
- payload: {
64
- pageKey: pageKey,
65
- page: page
66
- }
67
- };
68
- }
69
-
70
- function fetchDeferments(pageKey, defers) {
71
- if (defers === void 0) {
72
- defers = [];
73
- }
74
-
75
- pageKey = (0, _utils.urlToPageKey)(pageKey);
76
- return function (dispatch) {
77
- var fetches = defers.filter(function (_ref4) {
78
- var type = _ref4.type;
79
- return type === 'auto';
80
- }).map(function (_ref5) {
81
- var url = _ref5.url,
82
- _ref5$successAction = _ref5.successAction,
83
- successAction = _ref5$successAction === void 0 ? _actions.GRAFTING_SUCCESS : _ref5$successAction,
84
- _ref5$failAction = _ref5.failAction,
85
- failAction = _ref5$failAction === void 0 ? _actions.GRAFTING_ERROR : _ref5$failAction;
86
- var parsedUrl = new _urlParse["default"](url, true);
87
- var keyPath = parsedUrl.query.props_at;
88
- return dispatch((0, _requests.remote)(url, {
89
- pageKey: pageKey
90
- })).then(function () {
91
- dispatch({
92
- type: successAction,
93
- payload: {
94
- pageKey: pageKey,
95
- keyPath: keyPath
96
- }
97
- });
98
- })["catch"](function (err) {
99
- dispatch({
100
- type: failAction,
101
- payload: {
102
- url: url,
103
- err: err,
104
- pageKey: pageKey,
105
- keyPath: keyPath
106
- }
107
- });
108
- });
109
- });
110
- return Promise.all(fetches);
111
- };
112
- }
113
-
114
- function updateFragmentsUsing(page) {
115
- var changedFragments = {};
116
- page.fragments.forEach(function (fragment) {
117
- var type = fragment.type,
118
- path = fragment.path;
119
- changedFragments[type] = (0, _utils.getIn)(page, path);
120
- });
121
- return {
122
- type: _actions.UPDATE_FRAGMENTS,
123
- payload: {
124
- changedFragments: changedFragments
125
- }
126
- };
127
- }
128
-
129
- function saveAndProcessPage(pageKey, page) {
130
- return function (dispatch, getState) {
131
- pageKey = (0, _utils.urlToPageKey)(pageKey);
132
- var _page$defers = page.defers,
133
- defers = _page$defers === void 0 ? [] : _page$defers;
134
-
135
- if ((0, _utils.isGraft)(page)) {
136
- dispatch(handleGraft({
137
- pageKey: pageKey,
138
- page: page
139
- }));
140
- } else {
141
- dispatch(saveResponse({
142
- pageKey: pageKey,
143
- page: page
144
- }));
145
- }
146
-
147
- var hasFetch = typeof fetch != 'undefined';
148
-
149
- if (hasFetch) {
150
- return dispatch(fetchDeferments(pageKey, defers)).then(function () {
151
- if (page.fragments.length > 0) {
152
- var finishedPage = getState().pages[pageKey];
153
- return dispatch(updateFragmentsUsing(finishedPage));
154
- }
155
- });
156
- } else {
157
- return Promise.resolve();
158
- }
159
- };
160
- }