-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
pr05 Typescript #3: Migrate client/utils folder #3553
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
base: develop
Are you sure you want to change the base?
pr05 Typescript #3: Migrate client/utils folder #3553
Conversation
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.
Refactor the portions of using getConfig
for readability instead of inline
use new parseBoolean
function to parse the string val of getConfig
to a boolean instead
I wonder if this file was previously only checking if process.env.LOGIN_ENABLED
had a value??
So if the env file looked like:
LOGIN_ENABLED=false
UI_COLLECTIONS_ENABLED=false
getConfig('LOGIN_ENABLED') // "false"
!getConfig('LOGIN_ENABLED') // false
??
client/utils/consoleUtils.test.ts
Outdated
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.
tried my best with this test, I'm not 100% sure how the line offset works, so I didn't test its calculation, just that it should be a number in the tupple that's returned
client/utils/device.ts
Outdated
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.
PR > Files changed view on Github shows that this is a delete + new file, due to too many changes from the original due to refactoring, so let me know if it would be preferable to go back to the unrefactored!
I refactored for readability & handling the edgecase of not having a navigator or navigator.userAgent, but that might be excessive
client/utils/dispatcher.ts
Outdated
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.
Similar to above, GH shows this in PR mode as a deletion + new file, but it seems the history is still retained when you view git blame:
https://github.com/processing/p5.js-web-editor/blame/2362807016746d3b3798085437c2406db2d31757/client/utils/dispatcher.ts
Kept in the comments from a previous maintainer & added JSDocs. I think the number of new lines of jsdocs made it pass the diff tolerated percentage before github shows it as a file deletion instead of a file update
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 changes other than adding types & no refactors on this one
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.
Not 100% sure if these tests match the function's useage on previewEntry and if there are other cases that need to be added?
client/utils/formatDate.ts
Outdated
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.
Small refactor for readability + added JSDocs. History is retained despite looking like a delete + new file here
https://github.com/processing/p5.js-web-editor/blame/2362807016746d3b3798085437c2406db2d31757/client/utils/formatDate.ts
Did small refactor to return ''
early if !parsed
Also corrected what I thought was a typo here:
https://github.com/processing/p5.js-web-editor/pull/3553/files#diff-e2130c2674d12d8f2724335d2d8d2b7ade8d366efa4bf42717c2af4eec4fa6aaL28
const diffInMs = differenceInMilliseconds(now, parsed);
if (Math.abs(diffInMs < 10000)) {
return i18next.t('formatDate.JustNow');
} else if (diffInMs < 20000) {
return i18next.t('formatDate.15Seconds');
} else if (diffInMs < 30000) {
return i18next.t('formatDate.25Seconds');
} else if (diffInMs < 46000) {
return i18next.t('formatDate.35Seconds');
}
It seems like they're using Math.abs
incorrectly --> it's using Math.abs(true) // 1
and Math.abs(false) // 0
I updated to be :
const diffInMs = Math.abs(differenceInMilliseconds(now, parsed));
// rest of checks for what range the absolute diff between now and the time something happened falls under
client/utils/getConfig.ts
Outdated
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.
History retained here:
https://github.com/processing/p5.js-web-editor/blame/2362807016746d3b3798085437c2406db2d31757/client/utils/getConfig.ts
Refactors:
-
Remove the circular logic at
https://github.com/processing/p5.js-web-editor/pull/3553/files#diff-8fcb54386d32ff76c5399959faeb4e72bad616564277e8774dff9b20d9fa55b3L1-L4 -
Made an internal
_getConfig
function that doesn't warn when the value of the key doesn't exist --> use this to check if the environment is test or not -
Update
getConfig
to also takenullishString
as an option property --> if true, return''
instead ofundefined
when no value exists.
For when we want a number
or boolean
I added a new utility file called parseStringToType
below
- I was going to do the parsing within
getConfig
, as I did with my proof-of-concept PR from my grant application, but I thought actually keeping the parsing in a separate function keeps things more modular and testable & keeps the return types ofgetConfig
consistent for predictability
client/utils/language-utils.ts
Outdated
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.
History retained here:
https://github.com/processing/p5.js-web-editor/blame/2362807016746d3b3798085437c2406db2d31757/client/utils/language-utils.ts
Create a unit test to secure all behaviour for this function prior to refactor, then did a refactor to make the long for-loop & nested if-statements more readable
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.
Previous implementation for contxt:
client/utils/metaKey.js
Outdated
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 old implementation uses navigator.platform
which is deprecated:
https://developer.mozilla.org/en-US/docs/Web/API/Navigator/platform
I updated this file to use utils/device
to determine if the client is using a mac
client/utils/parseStringToType.ts
Outdated
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.
New utility functions to parse a string (from getConfig) to a specified type:
for parseNumber it returns a number if parseable or undefined/0 depending on the nullishNumber flag
for parseBoolean it returns a boolean if parseable or undefined/false depending on the nullishBoolean flag
client/utils/reduxFormUtils.ts
Outdated
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.
History retained here:
https://github.com/processing/p5.js-web-editor/blame/2362807016746d3b3798085437c2406db2d31757/client/utils/reduxFormUtils.ts
-
Moved the username regex to top of file for readability
-
Changed the username length check to
.length
instead of regex for readability: https://github.com/processing/p5.js-web-editor/pull/3553/files#diff-932633409951987d095611a326d0b6b624bd482b07f1e27a9ca7987f6b52a71bR32 -
Moved the internal helper functions higher, and grouped the public functions together on the bottom of the file
-
Added types -- let me know if this organisation makes sense as this was the most stylistic/open-ended thing I did on the PR
-
Also not sure if we want to have a root/types folder, or if it makes sense to keep types collocated where they are most used, and potentially exported, as I did here?
-
No other refactors in each function internally
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.
still working through the files but thought I'd sent a first wave!
@@ -15,7 +15,7 @@ import { | |||
} from './ide'; | |||
import { clearState, saveState } from '../../../persistState'; | |||
|
|||
const ROOT_URL = getConfig('API_URL'); | |||
const ROOT_URL = getConfig('API_URL', { nullishString: true }); |
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.
what do we think the "correct" or expected behavior would be if there was no API_URL? like is "/projects/${projectId}/zip" a valid ROOT_URL or would that cause a different error down the line? or in what cases is it undefined?
* Internal function to retrieve env vars, with no error handling. | ||
* @returns String value of env variable or undefined if not found. | ||
*/ | ||
function _getConfig(key: string): string | 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.
FYI i know we don't enforce this very consistently but i dont think we are meant to use underscores in naming, as we "follow" the airbnb guide https://github.com/airbnb/javascript?tab=readme-ov-file#naming--leading-underscore
@@ -307,6 +307,8 @@ export function cloneProject(project) { | |||
(file, callback) => { | |||
if ( | |||
file.url && | |||
S3_BUCKET && | |||
S3_BUCKET_URL_BASE && |
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.
good call!!
str?: string, | ||
nullishNumber = false | ||
): number | undefined { | ||
if (str == 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.
if we're letting string be typed as str?, then is undefined also a valid nullish value? should it be the more general if(!str)
?
str?: string, | ||
nullishBool = false | ||
): boolean | undefined { | ||
if (str == 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.
same here
* - see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/platform | ||
*/ | ||
export function isMac(): boolean { | ||
return typeof navigator?.userAgent === 'string' |
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.
do we need typeof
? why not something like this:
navigator?.userAgent?.toLowerCase().includes('mac') ?? false
} = {}; | ||
let frameIndex = 1; | ||
|
||
/** Codesandbox dispatcher message types */ |
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.
wonder if we can just use typescript enums here instead?
enum MessageTypes = {
START,
STOP,
etc
}
*/ | ||
export type Message = { | ||
type: MessageType, | ||
payload?: 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.
is there any way to use unknown
instead of any
?
function eventListener(e: MessageEvent) { | ||
const { data } = e; | ||
|
||
// should also store origin of parent? idk |
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 assume this is leftover from the js file but should we remove the dead code comments?
|
||
type EvalInClosureFn = (expr: string) => EvalResult; | ||
|
||
function __makeEvaluateExpression(evalInClosure: EvalInClosureFn) { |
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.
avoid using underscores for naming functions
function __makeEvaluateExpression(evalInClosure) { | ||
return (expr) => | ||
type EvalResult = { | ||
result: 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 haven't looked closely enough at where this is used, but is there any way to avoid using any
here as well? can we do unknown
and then cast it later after doing some type guard checks?
@@ -19,7 +26,11 @@ function evaluateExpression() { | |||
} | |||
result = (0, eval)(newExpr); // eslint-disable-line |
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.
can we change this eslint disable to be more specific to what it's disabling?
return null; | ||
} | ||
|
||
export default { |
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'm not sure exactly how this is getting imported but i wonder if we can just export these individually instead of in a dict wrapped as one default? totally understand that it's a holdover from the js file 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.
still working through the files but have a few more comments!
*/ | ||
function _getConfig(key: string): string | undefined { | ||
const env: Record<string, string | undefined> = | ||
(typeof global !== 'undefined' ? global : window)?.process?.env || {}; |
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.
why not do global !== 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.
or even
const configSource = global ?? window;
const env = configSource?.process?.env ?? {}
return env[key]; | ||
} | ||
|
||
type GetConfigOptions = { |
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.
should this be an interface? maybe using optional?
https://google.github.io/styleguide/tsguide.html#prefer-optional-over-undefined
return value; | ||
} | ||
|
||
export default getConfig; |
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.
same here can we convert to named export? https://google.github.io/styleguide/tsguide.html#exports
supportedLanguages: string[] = [], | ||
defaultLanguage: string = 'en' | ||
): string | undefined { | ||
if (typeof navigator === '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.
same here i think we should be able to do something like if (!navigator) return defaultLanguage
return defaultLanguage; | ||
} | ||
|
||
export default getPreferredLanguage; |
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.
can we put the default export on the function itself here too?
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.
or even better... maybe we can make it a named export instead
@@ -0,0 +1,156 @@ | |||
import i18n from 'i18next'; | |||
|
|||
/* eslint-disable */ |
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.
what are we disabling here? can we be more specific about the rule?
/** Processes form & mutates errors to add any `username` & `email` errors */ | ||
function validateUsernameEmail( | ||
formProps: Partial<UsernameAndEmail>, | ||
errors: Partial<FormErrors> |
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'm not sure i understand what happens when we do Partial<Partial<....>> do we need the double partial?
|
||
/** Validation for the Account Form */ | ||
export function validateSettings( | ||
formProps: Partial<AccountForm> |
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.
if AccountFormErrors is already Partial<AccountForm>
then can we also just use it for formProps?
export type LoginFormErrors = Partial<LoginForm>; | ||
|
||
/** Validation for the Login Form */ | ||
export function validateLogin(formProps: Partial<LoginForm>): LoginFormErrors { |
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.
same here, why not reuse LoginFormErrors? or rename it to be more general?
pr05 Typescript Migration 3: Migrate the client/utils folder
IMPORTANT: Should be reviewed after #3540 for clarity
Context:
Rebuild of Pr05/migrate client utils clairep94/p5.js-web-editor#5 --> forgot to do
git mv
for the first few files and directly renamed, so this looks like deleting and creating a new file for git, and erases the previous history.Migrate as much of the client/utils folder with the following steps:
git mv someUtil.js someUtil.ts
. If possible, commit without theno-verify
flag, otherwise withno-verify
.I forgot to add
no-verify
to my commit message in some of these initial onesChanges:
Please see annotations on files
Notes:
I have verified that this pull request:
npm run lint
)npm run test
)develop
branch.Fixes #123