{"id":17359201,"url":"https://github.com/deeleman/spaghettify","last_synced_at":"2025-04-15T00:30:49.171Z","repository":{"id":39003868,"uuid":"368682876","full_name":"deeleman/spaghettify","owner":"deeleman","description":"Enjoy an unparalleled SPA experience with Spaghettify, the ultimate JavaScript library that turns static web pages into fully functional SPA websites.","archived":false,"fork":false,"pushed_at":"2024-11-11T23:21:06.000Z","size":642,"stargazers_count":10,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-28T12:21:22.339Z","etag":null,"topics":["ajax","javascript","javascript-library","single-page-applications","spa","typescript","typescript-library","xhr","xhr-framework"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/deeleman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-05-18T22:36:25.000Z","updated_at":"2025-03-02T14:20:10.000Z","dependencies_parsed_at":"2024-02-22T09:31:20.389Z","dependency_job_id":"542c1792-4ace-43b9-af82-2ce264fbcd23","html_url":"https://github.com/deeleman/spaghettify","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deeleman%2Fspaghettify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deeleman%2Fspaghettify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deeleman%2Fspaghettify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deeleman%2Fspaghettify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/deeleman","download_url":"https://codeload.github.com/deeleman/spaghettify/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248983957,"owners_count":21193668,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ajax","javascript","javascript-library","single-page-applications","spa","typescript","typescript-library","xhr","xhr-framework"],"created_at":"2024-10-15T19:08:32.713Z","updated_at":"2025-04-15T00:30:49.140Z","avatar_url":"https://github.com/deeleman.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Spaghettify\n![GitHub](https://img.shields.io/github/license/deeleman/spaghettify?color=blue)\n![GitHub package.json version](https://img.shields.io/github/package-json/v/deeleman/spaghettify)\n![Travis (.com)](https://img.shields.io/travis/com/deeleman/spaghettify)\n[![Coverage Status](https://coveralls.io/repos/github/deeleman/spaghettify/badge.svg?branch=master)](https://coveralls.io/github/deeleman/spaghettify?branch=master)\n\nSpaghettify turns any static HTML site into a Single Page Application with AJAX-driven navigation and DOM elements sate persistence features. For doing so it implements a DOM interceptor and a _middleware-funneling processor_ that captures link click events, fetches each requested document via XHR and digests the response by streaming it through a series of middlware functions before refreshing the browser document.\n\nThese middleware functions are pluggable I/O handlers which observe the [Single Responsibility Principle](http://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html) and conform a full pipeline of steps, which can be categorized into `onBeforeComplete` middleware hooks, which DO NOT mutate the current page DOM, and `onAfterComplete` middleware hooks that do apply their changes (hence mutate) directly on the current page DOM after it has been injected.\n\nThe entire project is built on top of TypeScript and implements several polyfills and coding strategies to extend support to old legacy browsers, such as MSIE11.\n\n## Setting up your environment\nThe minimum requirements for running this project, either on development or production mode, and its development scripts are `node v12.16.0` and `npm v.6.14.15`, or later versions. Probably this project will run smoothly on older versions of `node` and `npm` but we recommend using the latest [LTS versions](https://nodejs.org/).\n\nThis project relies on [BabelJS](https://babeljs.io/) and [Webpack](https://https://webpack.js.org//) for compiling code in dev mode, running builds serve the demo site files and handling code optimisations.\n\nAll interaction with `BabelJS` and `Webpack` has been abstracted away in custom npm scripts for your convenience. \n\n### Installing dependencies\nAs a first step to spawn a development environment or production build, please run either `yarn` or `npm install` to pull all the required dependencies for this project.\n\n\n## Building the project for production\nPlease execute `yarn build` or `npm run build` from your terminal window. \n\nThe project bundler will navigate through the entire application tree and will build the JavaScript artifact into the `/dist` folder, bundled as `spaghettify.js`. Other useful bundles will be saved there as well for your convenience.\n\n\u003e **Can I fetch Spaghettify from the npm registry?** At the moment of this writing the project priorities are to increase test coverage a bit further and broaden up the API capabilities with extended support for user-provided middleware hooks. For the time being, Spaghettify is meant to be consumed as a browser dependency, but distributing it as a NPM package is in the roadmap. Please check back shortly for updates.\n\n## The Spaghettify API\n\nYou can instantiate and interact with Spaghettify through a convenient API catering with global toggles, route interceptors, exclusions and state persistence attribute flags and, last but not least, loading progress indicators and handlers.\n\nOnce you successfully compile Spaghettify, you can import and instantiate it into your application as follows:\n\n```html\n\u003cscript type=\"text/javascript\" src=\"/dist/spaghettify.js\"\u003e\u003c/script\u003e\n\u003cscript type=\"text/javascript\"\u003e\n  new Spaghettify({\n    enabled: true,\n    routes: ['*.html', 'content/*'],\n    excludeByAttr: 'no-spa',\n    loadProgress: true,\n    persistAttr: 'data-persist',\n  });\n\u003c/script\u003e\n```\n\nAs you can see Spaghettify can take a configuration object upon instantiation. Please note that **all fields are optional** and even the whole configuration object itself is also optional. If not provided, Spaghettify will be instantiated with the default options as described in the table below. \n\n### The Spaghettify settings API\n\nThe Spaghettify configuration settings object can be summarised as follows:\n\n\n|Field|Type|Default|Description|\n|---|---|---|---|\n|`enabled`|`Boolean`|`true`|Enables or disables Spaghettify upon instantiation|\n|`routes`|`String[]`|`['*']`|Defines patterns for routes to be intercepted and served through Spaghettify. Supports glob tokens.|\n|`excludeByAttr`|`String`|`undefined`|Defines an exclusion data attribute token (with or without the `data-` prefix). Links decorated with this attribute will be bypassed by Spaghettify|\n|`loadProgress`|`Boolean` `Function`|`false`|Enables a built-in progress bar or not. It can also take a function handler that will receive a percentage progress integer upon load. |\n|`persistAttr`|`String`|`undefined`|Defines an UI state persistence flag data attribute (with or without the `data-` prefix). Elements decorated with this attribute will persist their state across page navigation.  |\n\nPlease note that all configuration options (and the options payload itself) are optional and will take the default value if not explicitly declared.\n\n### Disposing Spaghettify after use\nSpaghettify interacts with your current document by internally binding event handlers to eligible links. In order to prevent memory leaks or if you want to stop Spaghettify until it is reinstated again you will want to destroy it as follows:\n\n```html\n\u003cscript type=\"text/javascript\"\u003e\n  // First, we instantiate Spaghettify\n  const spa = new Spaghettify();\n  // Then we dispose it after use\n  spa.destroy();\n\u003c/script\u003e\n```\n\n### Tip: Excluding links from being intercepted by Spaghettify\nAll links are configured by Spaghettify as subject to be intercepted. The internal events manager machinery will assess whether the link is eligible to be treated as an AJAX request or not by testing the link href value against the `routes` glob tokens.\n\nHowever, we can bypass this step upfront by configuring the `excludeByAttr` option with an attribute value, either with the `data-` prefix or not. \n\nNonetheless, and for the sake of semantics, Spaghettify will then only consider link elements configured with the full fledged attribute.\n\n```html\n\u003cscript type=\"text/javascript\"\u003e\n  new Spaghettify({\n    excludeByAttr: 'skip-me',\n  });\n\u003c/script\u003e\n\n\u003c!-- Spaghettify will disregard the following link --\u003e\n\u003ca href=\"foo.html\" data-skip-me\u003eSkip this link\u003c/a\u003e\n```\n\nThe configured attribute can be populated with any value or none at all. Spaghettify will disregard that value anyways.\n\n### Tip: Configuring custom load handlers\n\nAs we saw already, the `loadProgress` configuration option can take a `Boolean` primitive value or a _function handler_.\n\n```html\n\u003cscript type=\"text/javascript\"\u003e\n  new Spaghettify({\n    loadProgress: true,\n  });\n\u003c/script\u003e\n```\n\nIf not explicitly configured, or set to `false`, no progress bar indicator will be displayed. If provided as `true`, Spaghettify will show an animated red progress bar indicator on top of the viewport. The progress bar shows the actual download progress.\n\nHowever, consumers might want to implement their own visual solutions for rendering download progress information. Spaghettify gets them covered by providing a load progress handler that will expect an integer value parameter in its signature, which will take values from `0` to `100` as pages are requested and downloaded via HXR.\n\n```html\n\u003cscript type=\"text/javascript\"\u003e\n  new Spaghettify({\n    loadProgress: function onLoad(progress) {\n      console.log(progress); // Will log values from 0 to 100\n    },\n  });\n\u003c/script\u003e\n```\n\n### Tip: Persisting state across navigation\n\nSpaghettify implements an experimental API for persisting state in selected, annotated DOM nodes across page navigation. In order to do so you just need to configure a value token in the `persistAttr` option and then annotate those DOM elements whose state you want to be persisted with the equivalent `data-` attribute with an unique value each:\n\n```html\n\u003cscript type=\"text/javascript\"\u003e\n  new Spaghettify({\n    persistAttr: 'persist-id',\n  });\n\u003c/script\u003e\n\n\u003cinput type=\"text\" data-persist-id=\"my-input\" /\u003e\n```\n\nYou can explicitly prefix the value with `data-` or not, but Spaghettify will require you to annotate the DOM elements to be persisted with the full data attribute syntax.\n\n**Please note**: Attribute values are meant to be unique. Spaghettify will throw an exception if more than one element of different type is configured with the same attribute value.\n\nIt is worth highlighting that persistence will be applied on a full DOM `Node` basis, so it will encompass not only the element's inner HTML but also the native _touched_ state for input controls. And all this irrespective of the changes in the outer HTML.\n\n\n## Running Spaghettify in development mode\nYou can spawn a development environment by running `yarn dev` or `npm run dev` in the console.\n\nThe system will generate all the artifacts and serve the sandbox site (more details below) from http://localhost:3000 (or any other port of your choice if you append the `--port=PORT` param to the `dev` command, where `PORT` is the desired port) in _watch mode_, so the application will be recompiled upon changes in the source code.\n\n### The sandbox site\nThe sandbox site is a small, uber-simplistic web application that serves as a playground and testing arena for debugging Spaghettify in a live environment. It features a pretty simplistic styling, through a set of different, hierarchical pages depicting the following key features:\n\n- The main `index.html` contains an instance of Spaghettify inline for demo purposes. All the other documents implement such instance as an imported script. You do not need to import Spaghettify on each document, only the entry one. However, this allows to fire up Spaghettify from any document after reloading the browser window for demo purposes. In a real production scenario, Spaghettify can (and should) be imported and instantiated only once in the entry location.\n- Links to Page B are configured to bypass Spaghettify and force a full page load. Since it is imported there, Spaghettify will take over navigation from there.\n- Content is scattered in between the `/sandbox` root level and a child `/sandbox/content` subfolder so contributors can play around with link selectors pointing to subfolders, if necessary.\n- The main body contains dummy text with slight variations to ensure the page transition is noticeable.\n- The side menu contains diverse input controls to validate state persistence accross navigated pages.\n- Page A and pages within `/sandbox/content` feature either inline or imported custom JavaScript that Spaghettify will digest, reinject and execute when required.\n- Spaghettify supports forward and backwards history navigation. Browse around and give it a shot!\n### Linting your code contributions\n\nESLint is currently enabled in the Spaghettify codebase and a linting audit will be triggered upon building the project. You can configure your IDE to automatically provide linting assessment as you introduce changes. Moreover, you can trigger code linting anytime by running `npm run lint` or `yarn lint` in your terminal console.\n\n### Testing your code contributions\n\nYou can introduce tests in the codebase or execute the existing ones by running `npm test` or `yarn test` in your terminal console. Code coverage data is collected and stored in a conveniently formatted document at `/coverage/lcov-report`. For on-screen coverage reporting please append the `--coverage` param to the `test` command.\n\nYou can also check online a comprehensive test coverage report at [Coveralls](https://coveralls.io/github/deeleman/spaghettify).\n\n## Distributed under the MIT License\n\nCopyright 2021 Pablo Deeleman\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeeleman%2Fspaghettify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeeleman%2Fspaghettify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeeleman%2Fspaghettify/lists"}