You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1 line
19 KiB

{"ast":null,"code":"import { nanoid } from 'nanoid/non-secure';\nexport default function createMemoryHistory() {\n var index = 0;\n var items = [];\n var pending = [];\n var interrupt = function interrupt() {\n pending.forEach(function (it) {\n var cb = it.cb;\n it.cb = function () {\n return cb(true);\n };\n });\n };\n var history = {\n get index() {\n var _window$history$state;\n var id = (_window$history$state = window.history.state) === null || _window$history$state === void 0 ? void 0 : _window$history$state.id;\n if (id) {\n var _index = items.findIndex(function (item) {\n return item.id === id;\n });\n return _index > -1 ? _index : 0;\n }\n return 0;\n },\n get: function get(index) {\n return items[index];\n },\n backIndex: function backIndex(_ref) {\n var path = _ref.path;\n for (var i = index - 1; i >= 0; i--) {\n var item = items[i];\n if (item.path === path) {\n return i;\n }\n }\n return -1;\n },\n push: function push(_ref2) {\n var path = _ref2.path,\n state = _ref2.state;\n interrupt();\n var id = nanoid();\n items = items.slice(0, index + 1);\n items.push({\n path: path,\n state: state,\n id: id\n });\n index = items.length - 1;\n window.history.pushState({\n id: id\n }, '', path);\n },\n replace: function replace(_ref3) {\n var _ref4;\n var _window$history$state2;\n var path = _ref3.path,\n state = _ref3.state;\n interrupt();\n var id = (_ref4 = (_window$history$state2 = window.history.state) === null || _window$history$state2 === void 0 ? void 0 : _window$history$state2.id) != null ? _ref4 : nanoid();\n var pathWithHash = path;\n if (!items.length || items.findIndex(function (item) {\n return item.id === id;\n }) < 0) {\n pathWithHash = pathWithHash + location.hash;\n items = [{\n path: pathWithHash,\n state: state,\n id: id\n }];\n index = 0;\n } else {\n if (items[index].path === path) {\n pathWithHash = pathWithHash + location.hash;\n }\n items[index] = {\n path: path,\n state: state,\n id: id\n };\n }\n window.history.replaceState({\n id: id\n }, '', pathWithHash);\n },\n go: function go(n) {\n interrupt();\n var nextIndex = index + n;\n var lastItemIndex = items.length - 1;\n if (n < 0 && !items[nextIndex]) {\n n = -index;\n index = 0;\n } else if (n > 0 && nextIndex > lastItemIndex) {\n n = lastItemIndex - index;\n index = lastItemIndex;\n } else {\n index = nextIndex;\n }\n if (n === 0) {\n return;\n }\n return new Promise(function (resolve, reject) {\n var done = function done(interrupted) {\n clearTimeout(timer);\n if (interrupted) {\n reject(new Error('History was changed during navigation.'));\n return;\n }\n var title = window.document.title;\n window.document.title = '';\n window.document.title = title;\n resolve();\n };\n pending.push({\n ref: done,\n cb: done\n });\n var timer = setTimeout(function () {\n var index = pending.findIndex(function (it) {\n return it.ref === done;\n });\n if (index > -1) {\n pending[index].cb();\n pending.splice(index, 1);\n }\n }, 100);\n var onPopState = function onPopState() {\n var _window$history$state3;\n var id = (_window$history$state3 = window.history.state) === null || _window$history$state3 === void 0 ? void 0 : _window$history$state3.id;\n var currentIndex = items.findIndex(function (item) {\n return item.id === id;\n });\n index = Math.max(currentIndex, 0);\n var last = pending.pop();\n window.removeEventListener('popstate', onPopState);\n last === null || last === void 0 ? void 0 : last.cb();\n };\n window.addEventListener('popstate', onPopState);\n window.history.go(n);\n });\n },\n listen: function listen(listener) {\n var onPopState = function onPopState() {\n if (pending.length) {\n return;\n }\n listener();\n };\n window.addEventListener('popstate', onPopState);\n return function () {\n return window.removeEventListener('popstate', onPopState);\n };\n }\n };\n return history;\n}","map":{"version":3,"sources":["createMemoryHistory.tsx"],"names":["nanoid","createMemoryHistory","index","items","pending","interrupt","forEach","it","cb","history","id","window","state","findIndex","item","get","backIndex","path","i","push","slice","length","pushState","replace","pathWithHash","location","hash","replaceState","go","n","nextIndex","lastItemIndex","Promise","resolve","reject","done","interrupted","clearTimeout","timer","Error","title","document","ref","setTimeout","splice","onPopState","currentIndex","Math","max","last","pop","removeEventListener","addEventListener","listen","listener"],"mappings":"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","sourceRoot":"../../src","sourcesContent":["import { nanoid } from 'nanoid/non-secure';\nexport default function createMemoryHistory() {\n let index = 0;\n let items = [];\n\n // Pending callbacks for `history.go(n)`\n // We might modify the callback stored if it was interrupted, so we have a ref to identify it\n const pending = [];\n const interrupt = () => {\n // If another history operation was performed we need to interrupt existing ones\n // This makes sure that calls such as `history.replace` after `history.go` don't happen\n // Since otherwise it won't be correct if something else has changed\n pending.forEach(it => {\n const cb = it.cb;\n it.cb = () => cb(true);\n });\n };\n const history = {\n get index() {\n var _window$history$state;\n // We store an id in the state instead of an index\n // Index could get out of sync with in-memory values if page reloads\n const id = (_window$history$state = window.history.state) === null || _window$history$state === void 0 ? void 0 : _window$history$state.id;\n if (id) {\n const index = items.findIndex(item => item.id === id);\n return index > -1 ? index : 0;\n }\n return 0;\n },\n get(index) {\n return items[index];\n },\n backIndex(_ref) {\n let {\n path\n } = _ref;\n // We need to find the index from the element before current to get closest path to go back to\n for (let i = index - 1; i >= 0; i--) {\n const item = items[i];\n if (item.path === path) {\n return i;\n }\n }\n return -1;\n },\n push(_ref2) {\n let {\n path,\n state\n } = _ref2;\n interrupt();\n const id = nanoid();\n\n // When a new entry is pushed, all the existing entries after index will be inaccessible\n // So we remove any existing entries after the current index to clean them up\n items = items.slice(0, index + 1);\n items.push({\n path,\n state,\n id\n });\n index = items.length - 1;\n\n // We pass empty string for title because it's ignored in all browsers except safari\n // We don't store state object in history.state because:\n // - browsers have limits on how big it can be, and we don't control the size\n // - while not recommended, there could be non-serializable data in state\n window.history.pushState({\n id\n }, '', path);\n },\n replace(_ref3) {\n var _window$history$state2;\n let {\n path,\n state\n } = _ref3;\n interrupt();\n const id = ((_window$history$state2 = window.history.state) === null || _window$history$state2 === void 0 ? void 0 : _window$history$state2.id) ?? nanoid();\n\n // Need to keep the hash part of the path if there was no previous history entry\n // or the previous history entry had the same path\n let pathWithHash = path;\n if (!items.length || items.findIndex(item => item.id === id) < 0) {\n // There are two scenarios for creating an array with only one history record:\n // - When loaded id not found in the items array, this function by default will replace\n // the first item. We need to keep only the new updated object, otherwise it will break\n // the page when navigating forward in history.\n // - This is the first time any state modifications are done\n // So we need to push the entry as there's nothing to replace\n pathWithHash = pathWithHash + location.hash;\n items = [{\n path: pathWithHash,\n state,\n id\n }];\n index = 0;\n } else {\n if (items[index].path === path) {\n pathWithHash = pathWithHash + location.hash;\n }\n items[index] = {\n path,\n state,\n id\n };\n }\n window.history.replaceState({\n id\n }, '', pathWithHash);\n },\n // `history.go(n)` is asynchronous, there are couple of things to keep in mind:\n // - it won't do anything if we can't go `n` steps, the `popstate` event won't fire.\n // - each `history.go(n)` call will trigger a separate `popstate` event with correct location.\n // - the `popstate` event fires before the next frame after calling `history.go(n)`.\n // This method differs from `history.go(n)` in the sense that it'll go back as many steps it can.\n go(n) {\n interrupt();\n\n // To guard against unexpected navigation out of the app we will assume that browser history is only as deep as the length of our memory\n // history. If we don't have an item to navigate to then update our index and navigate as far as we can without taking the user out of the app.\n const nextIndex = index + n;\n const lastItemIndex = items.length - 1;\n if (n < 0 && !items[nextIndex]) {\n // Attempted to navigate beyond the first index. Negating the current index will align the browser history with the first item.\n n = -index;\n index = 0;\n } else if (n > 0 && nextIndex > lastItemIndex) {\n // Attempted to navigate past the last index. Calculate how many indices away from the last index and go there.\n n = lastItemIndex - index;\n index = lastItemIndex;\n } else {\n index = nextIndex;\n }\n if (n === 0) {\n return;\n }\n\n // When we call `history.go`, `popstate` will fire when there's history to go back to\n // So we need to somehow handle following cases:\n // - There's history to go back, `history.go` is called, and `popstate` fires\n // - `history.go` is called multiple times, we need to resolve on respective `popstate`\n // - No history to go back, but `history.go` was called, browser has no API to detect it\n return new Promise((resolve, reject) => {\n const done = interrupted => {\n clearTimeout(timer);\n if (interrupted) {\n reject(new Error('History was changed during navigation.'));\n return;\n }\n\n // There seems to be a bug in Chrome regarding updating the title\n // If we set a title just before calling `history.go`, the title gets lost\n // However the value of `document.title` is still what we set it to\n // It's just not displayed in the tab bar\n // To update the tab bar, we need to reset the title to something else first (e.g. '')\n // And set the title to what it was before so it gets applied\n // It won't work without setting it to empty string coz otherwise title isn't changing\n // Which means that the browser won't do anything after setting the title\n const {\n title\n } = window.document;\n window.document.title = '';\n window.document.title = title;\n resolve();\n };\n pending.push({\n ref: done,\n cb: done\n });\n\n // If navigation didn't happen within 100ms, assume that it won't happen\n // This may not be accurate, but hopefully it won't take so much time\n // In Chrome, navigation seems to happen instantly in next microtask\n // But on Firefox, it seems to take much longer, around 50ms from our testing\n // We're using a hacky timeout since there doesn't seem to be way to know for sure\n const timer = setTimeout(() => {\n const index = pending.findIndex(it => it.ref === done);\n if (index > -1) {\n pending[index].cb();\n pending.splice(index, 1);\n }\n }, 100);\n const onPopState = () => {\n var _window$history$state3;\n const id = (_window$history$state3 = window.history.state) === null || _window$history$state3 === void 0 ? void 0 : _window$history$state3.id;\n const currentIndex = items.findIndex(item => item.id === id);\n\n // Fix createMemoryHistory.index variable's value\n // as it may go out of sync when navigating in the browser.\n index = Math.max(currentIndex, 0);\n const last = pending.pop();\n window.removeEventListener('popstate', onPopState);\n last === null || last === void 0 ? void 0 : last.cb();\n };\n window.addEventListener('popstate', onPopState);\n window.history.go(n);\n });\n },\n // The `popstate` event is triggered when history changes, except `pushState` and `replaceState`\n // If we call `history.go(n)` ourselves, we don't want it to trigger the listener\n // Here we normalize it so that only external changes (e.g. user pressing back/forward) trigger the listener\n listen(listener) {\n const onPopState = () => {\n if (pending.length) {\n // This was triggered by `history.go(n)`, we shouldn't call the listener\n return;\n }\n listener();\n };\n window.addEventListener('popstate', onPopState);\n return () => window.removeEventListener('popstate', onPopState);\n }\n };\n return history;\n}\n//# sourceMappingURL=createMemoryHistory.js.map"]},"metadata":{},"sourceType":"module"}