-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: added extension-load-queue #8
Conversation
@@ -3,6 +3,6 @@ module.exports = { | |||
extends: require.resolve('@diplodoc/lint/eslint-config'), | |||
parserOptions: { | |||
tsconfigRootDir: __dirname, | |||
project: ['./tsconfig.json', './tsconfig.tests.json'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code will be deleted every time by a pre-commit hook
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I will change it in @diplodoc/lint package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this PR, I went another way - I created an alternative TS configuration for the build.
scripts/bundle.js
Outdated
nodeExternalsPlugin(), | ||
], | ||
}); | ||
|
||
build({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH, I feel like making a clear distinction between react
and non-react
parts of code in this package is a particularly debatable decision. I'd vouch for just keeping it simple — that way, ELQ's react and non-react parts should probably reside in the same folder.
Also, while this is out of scope for this PR, I'd also argue that using ESBuild in the case of this particular package is also a pretty uncanny decision; in my opinion, it's better to ship all utility packages as a non-bundled set of TypeScript sources, tree shaking ought to do quite literally everything else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH, I feel like making a clear distinction between react and non-react parts of code in this package is a particularly debatable decision. I'd vouch for just keeping it simple — that way, ELQ's react and non-react parts should probably reside in the same folder.
I thought like this: hooks for React require the import of a React package. At the same time, the rest of the utilities seem to be more like a set of simple functions. I would like to be able to use non-React utilities separately. Additionally, it seems to me that other packages in diplodoc-platform are structured in a similar manner. But I don't have a strong opinion here.
Also, while this is out of scope for this PR, I'd also argue that using ESBuild in the case of this particular package is also a pretty uncanny decision; in my opinion, it's better to ship all utility packages as a non-bundled set of TypeScript sources, tree shaking ought to do quite literally everything else.
Interesting thoughts. I suggest solving them separately, not within the framework of this PR.
src/lib/extension-load-queue.ts
Outdated
|
||
export type ControllerLoadedCallback<T> = (controller: T) => void; | ||
|
||
export const QUEUE_SYMBOL: unique symbol = Symbol.for('queue'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to specify unique symbol
explicitly, the Symbol.for
statement is already inferred this way
src/lib/extension-load-queue.ts
Outdated
@@ -0,0 +1,86 @@ | |||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |||
type BrowserWindow = any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather use declaration merging here in lieu of specifying any
explicitly.
onQueueCreated?: (created: boolean) => void; | ||
} | ||
|
||
export const isBrowser = () => typeof window !== 'undefined' && typeof document !== 'undefined'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I imagine, isBrowser
should be a very frequently used utility, so if we're at all serious about this package and its usage, i'd place this in a separate top-level file and would make sure to document this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, at the same time, the current version implies use in place - I'll leave it like that for now, or rename it. I'll also add a comment about correcting it in the future.
src/lib/extension-load-queue.ts
Outdated
|
||
export const QUEUE_SYMBOL: unique symbol = Symbol.for('queue'); | ||
|
||
export type ScriptStore<T> = ControllerLoadedCallback<T>[] | null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't throwing an error ("This should only be used client-side") make more sense here though? In particular, it would allow to keep the API a bit more consistent without that null
src/react/extension-load-queue.ts
Outdated
if (store) { | ||
store.push(setController); | ||
|
||
return () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, I feel like it might be a better idea to auto-cleanup already fulfilled controller requests when processing the queue. Up for debate, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Up for debate, though.
That's right. For now, I just want to put the general code into the utils.
.eslintignore
Outdated
@@ -5,6 +5,7 @@ | |||
.DS_Store | |||
node_modules | |||
/lib | |||
/react |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is extra at this point
src/lib/extension-load-queue.ts
Outdated
declare global { | ||
interface Window { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
[key: symbol]: any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is still sub-optimal, even though I won't mind as much if you decide to leave this as is. An optimal typing strategy would be something like https://github.com/diplodoc-platform/html-extension/blob/dea1790907586c976d0ec3cf1a7f8dae7f1859ea/src/types.ts#L28
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the example given, GLOBAL_SYMBOL is a pre-known constant. In mine, it is a key that is not known in advance. For the same reason, ScriptStore is generic in this PR. I propose to solve the problem of improved typing through future PR.
|
||
export const getQueueStore = () => { | ||
if (isBrowser()) { | ||
(window as Window)[QUEUE_SYMBOL] = (window as Window)[QUEUE_SYMBOL] || false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as Window
casts are weird — did you encounter any issues with declaration merging?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, unfortunately, I still face a set of problems. I can't get rid of this cast. I propose to solve the problem of improved typing through future PR.
@@ -0,0 +1,24 @@ | |||
import {AttrsParser} from './attrs'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels like this file and extension-load-queue.ts
should be put in their own respective directory.
Then you could use export * from './extension-load-queue';
in this file, which would be much tidier if more utilities are added.
The mermaid, latex and HTML packages use the mechanism of asynchronous loading of runtime scripts. This process is implemented via a queue and by adding a field to the window object. This PR code is incorporated into a set of functions that can be used in any Diplodoc extension package.To protect against external access, the Symbol data type has been used instead of a string. The code has already been tested and debugged in the HTML extension
Because the
.eslintrc.js
file is generated using a pre-commit hook and thelint
,typecheck
andbuild
scripts requires different TS configurations, a separate TS configuration file (tsconfig.build.json
) has been created.