So I'm using a module that queries content based on its type and to figure out what content it is looking for, it must first build the query string by using various functions like Not, And, type, typeIs etc. One of these functions is causing me issues in production; Mainly the typeIs function. Here is how it is implemented.
public typeIs<TNewType>(newTypeAssertion: new (...args: any[]) => TNewType) {
this.stringValue = `TypeIs:${newTypeAssertion.name}` // Here is the issue
this.segmentType = 'typeIs'
return this.finialize<TNewType>()
}
Now the problem with this is that when the code gets minified, by default all function names will get compressed, mangled etc. To solve this problem I set my minimizer to keep function names. Here is how it looks.
minimizer: [
new TerserPlugin({
cache: true,
parallel: true,
terserOptions: {
keep_fnames: true
}
})
]
This works great in chrome but the problem is that this doesn't work in edge.
Here is a screenshot to show what I mean.
You can see that I've highlighted the word Typeis: in the image and the reason for this is because that is the part of the query string that is malformed. It should have been Typeis:User for it to work properly.
How I solved this isdue was by setting my minimizer to look like this:
minimizer: [
new TerserPlugin({
cache: true,
parallel: true,
terserOptions: {
keep_fnames: true,
compress: false // This setting is crucial for it work on edge.
}
})
]
and now here is how it looks on Edge.
As you can see, now the query string is no longer malformed because there was no compression. Obviously I want my code to be compressed to lower my bundle size and have gone through many of its compress options to figure out which option is the culprit but to no avail.
My question is, why does the name property output nothing in the Edge browser when compressed and not in chrome?
The comment made by user kzc in this github issued helped me solved this problem(https://github.com/terser-js/terser/issues/120).
In his comment, he mentioned that it was a bug related to these 6 compression options
collapse_vars
inline=3
join_vars
loops
reduce_vars
sequences (depending on the code)
unused
After finding that out, it was just matter of looking at the output bundle and making some logically deductions as to which one caused this issue. Turns out it was the reduce_vars option.
Now my minimizer looks like this
minimizer: [
new TerserPlugin({
cache: true,
parallel: true,
terserOptions: {
mangle: true,
compress: {
reduce_vars: false
},
keep_fnames: true
}
})
]
Looking at the terser documentation for its compression options, this is what reduce_vars does.
reduce_vars (default: true) -- Improve optimization on variables assigned with and used as constant values.
which makes sense as to why setting it to false solved this issue. At the top of the file my output bundle consisted of constant variables that hold references to modules that I imported and at some point in the compression stage, it reduced those variables so much that it probably couldn't figure out what module that variable belong to. That is just what I think and maybe someone else can chime in on this issue.
Related
I recently upgraded my project to TypeScript 4.4.3 from 3.9.9.
My project's using "strictNullChecks": true, in its tsconfig.json, and runs in the browser, not server-side on Node.
In TypeScript 4.4.3, it seems like the type declarations for top has changed to WindowProxy | null (node_modules/typescript/lib/lib.dom.d.ts)
This means that I get the following error1 wherever I try to access properties of top2: TS Playground
const topUrl = top.window.location.href; // => Object is possibly 'null'.
How can I ignore this category of errors only for when top is possibly null?3
1 I understand that this error is warning me against the scenario where my website is loaded in an iframe, and therefore can't access top due to XSS. This isn't an issue because my 'X-Frame-Options' is set to 'sameorigin' and will therefore refuse to load my website in a cross-origin iframe.
2 I access properties of top because I use iframes inside my project a lot, where it loads sub-pages on the same domain.
3 I could use the following fixes to get around this Object is possibly 'null'., but I'd prefer not to, as my project is quite large and this fix would be tedious with minimal improvement.
let topUrl = top?.window.location.href || '';
let topUrl = '';
if (top) {
topUrl = top.window.location.href;
}
I could also ignore these errors on every line with // #ts-ignore, but there's a lot of references to top and I don't want to clutter the project (also, other TypeScript errors on the same line would be ignored).
// #ts-ignore
const topUrl = top.window.location.href;
I found a solution which would possibly fit your needs. And there are 2 versions of the solution you can take into consideration.
Both of these versions work by overriding the built-in lib-dom with a npm package #types/web which is also provided by Microsoft.
Beta but systematic one - Using the latest official 'lib override' from typescript#4.5.0-beta
Follow steps below and things are gonna work as you expect without any other code modifications:
Upgrade to TypeScript 4.5.0:
npm i -D typescript#4.5.0-beta
or install globally
npm i -g typescript#4.5.0-beta
Install the #types/web#0.0.1 type package which has top: Window type
npm i -D #typescript/lib-dom#npm:#types/web#0.0.1
I have made some simple tests on this solution and managed to get behaviour you want.
The only shortcoming of this solution is that typescript#4.5 is still beta currently. But It worth your consideration since its final release will be just on next month.
TypeScript 4.5 Iteration Plan
Stable one - typescript 4.4.3 and switch the built-in dom lib.
install #types/web
npm i -D #types/web#0.0.1
notice that the install command is different from the above one.
Update your tsconfig.json. There are two cases to consider depending on if you have lib defined in your tsconfig.json or not.
Without "lib" - You will need to add "lib": []. The value you want to add inside your lib should correlate to your "target". For example if you had "target": "es2017", then you would add "lib": ["es2017"]
With "lib" - You should remove "dom".
The drawback of this second version of solution is, it cannot prevent your dependencies in node_modules from pulling in the TypeScript DOM library.
Please bear in mind that despite #types/web is up to version 0.0.40, only version 0.0.1 of #types/web has top typed top: Window instead of top: WindowProxy | null which is what you need.
The problem
You decided to upgrade your compiler version, and, as mentioned in a comment, major software version changes almost always come with breaking API changes.
The correct way to solve your issue (prevent compiler errors) is to modify your source code to satisfy the compiler. You said that modifying your source code in this way would be a chore, and asked about modifying the compiler configuration instead such that you can avoid modifying your source code.
It is not possible to override the types in lib.dom.d.ts in new type declarations. TypeScript will emit additional errors if you attempt to do this, even if you disable type-checking of your new declaration file, resulting in an incompatible merging of your new declarations. Your only option is to exclude the built-in DOM library and provide your own modified version of it.
Here is an overview of how to do that:
Starting TSConfig
You haven't provided your tsconfig.json file, so here's an example to use as a base, with the assumption that your source is organized in your repo's src/ directory:
Note: "strict": true implies "strictNullChecks": true
{
"compilerOptions": {
"isolatedModules": true,
"lib": [
"esnext",
"dom",
"dom.iterable"
],
"module": "esnext",
"outDir": "dist",
"strict": true,
"target": "esnext"
},
"include": [
"./src/**/*"
]
}
Creating the modified lib.dom.d.ts library
First download the lib.dom.d.ts file from the tag that matches your TypeScript version (4.4.3): https://github.com/microsoft/TypeScript/blob/v4.4.3/lib/lib.dom.d.ts
Move the file to src/types/lib.dom.d.ts in your project
Remove the triple-slash reference on line 18 by deleting the entire line. (This will allow you to continue using other built-in libraries.)
Modify line 17286 from this:
readonly top: WindowProxy | null;
to this:
readonly top: WindowProxy;
Modify line 18350 from this:
declare var top: WindowProxy | null;
to this:
declare var top: WindowProxy;
Save the file
Modifying your TSConfig
Now that you have a replacement library for the DOM types in your program, you need to tell the compiler to use it that way. Here's what you need to change:
{
"compilerOptions": {
// ...
"lib": [
"esnext",
"dom", // Delete this from the array
"dom.iterable"
],
// ...
// Add this array property
"typeRoots": [
"./node_modules/#types",
"./src/types"
]
},
// ...
}
So the modified tsconfig.json now looks like this:
{
"compilerOptions": {
"isolatedModules": true,
"lib": [
"esnext",
"dom.iterable"
],
"module": "esnext",
"outDir": "dist",
"strict": true,
"target": "esnext",
"typeRoots": [
"./node_modules/#types",
"./src/types"
]
},
"include": [
"./src/**/*"
]
}
Conclusion
That's it. Now you should be able to compile your program and reference window.top or just the global top as a non-nullable value without a compiler error.
You'll need to repeat this process every time you upgrade TypeScript. Is this strategy more sustainable than modifying your source code? That's up to you.
I preface this answer with a strong warning that I would not do this to my project and encourage anyone in this position to fix the errors the proper way using null coalescing or not null assertion. EG:
window.top!.scrollTo()
top!.scrollTo()
window.top?.scrollTo()
top?.scrollTo()
// etc..
Even though theres 1500 I think using some regular expression you could easily target a large portion of those errors and fix with ease. With that said heres some other options:
I havent done this in a production project and might result in some other strange errors, its largely untested by myself outside of quick testing
The summary of this solution is you could clone the lib.dom.ts file and make the modifications by hand.
Copy ./node_modules/typescript/lib/lib.dom.d.ts to somewhere in your project, say ./lib.dom.modified-4.4.3.d.ts
Make the modifications to remove the null type from window.top and top types
// old
// readonly top: WindowProxy | null;
// new
readonly top: WindowProxy;
...
// old
// declare var top: WindowProxy | null;
// new
declare var top: WindowProxy;
Update your tsconfig.json to remove dom as one of the libraries and add it to the list of types
{
"compilerOptions": {
"lib": [
"ES6"
],
"strictNullChecks": true,
"module": "commonjs",
"target": "ES6",
"types": [
"./lib.dom.modified-4.4.3"
]
},
"include": [
"src/**/*"
]
}
Now you have a custom dom library with the top property not nullable
Alternatively you could make a patch for lib-dom using git and apply it post install. Details about how to do that are outlined in several solutions of this question How to overwrite incorrect TypeScript type definition installed via #types/package
You can initialize a VCS if you have not already done so. Then
look at the place of your error
see what you would need to replace it to
use whatever tools you use to replace all occurrences of the source text to the target text
if there are still errors, repeat
Once you have replaced all occurrences of issues this way, you will need to review your changes. You will find the changes via the VCS. If you use git, then the command is
git diff
See all the changes and whichever looks even a little bit suspect, investigate and see whether the automatic change was correct. If not, perform whatever you need to ensure that your code is correct.
Test everything. You would do well if you would create a separate versioning branch for this work which would be tested for a long time before it's being released to production.
instead that you shoud use !, that typescript ignores the fact that the value could be null which in your case it is not
const topUrl = top!.window.location.href;
if your ES-LINT complains on that you can set the in config file like that
module.exports = {
...
rules: {
...
'#typescript-eslint/no-non-null-assertion': 'off'
},
...
}
I access properties of top because I use iframes inside my project a lot, where it loads sub-pages on the same domain.
top is potentially null...
This isn't an issue because my 'X-Frame-Options' is set to 'sameorigin' and will therefore refuse to load my website in a cross-origin iframe.
But you're saying that's impossible, in which case...
function getTop(): NonNullable<typeof top> {
if (!top) throw new Error('global var top is null');
return top;
}
...then replace any occurrence of top.example with getTop().example so as to centralize all potential 'null' errors.
While this isn't the most simple solution, it should be the safest.
In your question, you state:
I could use the following fixes to get around this Object is possibly 'null'., but I'd prefer not to, as my project is quite large and this fix would be tedious with minimal improvement.
let topUrl = top?.window.location.href || '';
I can appreciate the tedious nature of this task, but if you're insistent on using TypeScript, I must also be insistent that you employ this solution. It is necessary in TypeScript.
One way I would solve this problem would be to use my code editor/IDE program to search/replace all text references in my project. I use Visual Studio Code which allows me to Search and Replace specific text in my source files. It allows for Regex searching, including and excluding particular files. I'm certain that a great majority of code editors/IDEs have similar functionality.
im desperate, finaly made myself study some react and guess what, cant even start cuz suddenly my settings are messed up. When ever I try to save to auto format nothing happens. I really tried everything from here on stackoverflow and even yt but nothing really helped...Gonna show you my code :this is how it looks like after I press ctrl+s to save the file and to format it...
Like a week ago everything worked well and somehow I messed up the settings. Would be helpful if someone sent me his settings.json because Ive tried some Ive found on google and none helped. I really wanna study React but this wont let me. I know I can use codesandblox but its not it...Thx in advance...
You need to set your default formatter to Prettier and enable format on save.
Default Formatter
To ensure that this extension is used over other extensions you may have installed, be sure to set it as the default formatter in your VS Code settings. This setting can be set for all languages or by a specific language. The following will use Prettier for only Javascript.
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
Format On Save
Respects editor.formatOnSave setting.
You can turn on format-on-save on a per-language basis by scoping the setting:
// Set the default
"editor.formatOnSave": false,
// Enable per-language
"[javascript]": {
"editor.formatOnSave": true
}
{
"git.autofetch": true,
"terminal.explorerKind": "external",
"files.autoSave": "afterDelay",
"terminal.integrated.profiles.windows":{"Git Bash":{"path":"C:\\Program Files\\Git\\bin\\bash.exe"}, },
"terminal.integrated.defaultProfile.windows": "Git Bash",
"editor.formatOnSave": true,
"html.format.indentHandlebars": true,
"html.format.indentInnerHtml": true,
"settingsSync.ignoredExtensions": [
],
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
}
When I configure my vscode with eslint & prettier, I met a problem in .settings.json file with error message "Auto Fix is enabled by default. Use the single string form.":
My eslint configuration is:
My prettier configuration is:
module.exports = {
singleQuote: true,
semi: false
}
Does anybody know what's the reason and how to fix?
It seems a tab width issue, try add "tabWidth": 4 in your prettier config.
EDIT:
According to ESLint Reference: "eslint.validate" is an array of language identifiers specifying the files for which validation is to be enforced.
"eslint.validate" accept an array of language identifiers, not an array of objects.
No need for "autoFix", it defaults to be true.
So your settings should be:
"eslint.validate": [
"vue",
"html",
"javascript"
]
THIS IS SOLVED ON GOOGLE SIDE NOW
Google Cloud NodeJs library now fix integrated. Keeping this question just for reference.
ORIGINAL QUESTION
I wanted my code to be clean(er) and used Typescript & async/await writing Cloud Functions accessing Google Cloud Storage.
Some important parts of my tsconfig.json:
{
"compilerOptions": {
"declaration": false,
"target": "es6",
"module": "commonjs",
"noImplicitAny": true,
"removeComments": true,
"noUnusedLocals": true,
"moduleResolution": "node",
"sourceMap": false,
"typeRoots": [
"node_modules/#types"
],
"lib": [
"es2016"
],
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"outDir": "./"
}
}
First error I had was ECONNRESET and I was making a call like this which probably caused it:
await bucket.file(path).download({
destination: tempFilePath
})
I've figured rest of the function did not wait till this line to be finished and whole function execution ends before file is downloaded from GCS to temp path.
So I've put that section into try/catch block:
try {
await bucket.file(path).download({
destination: tempFilePath
})
} catch (error) {
console.log(error);
}
It worked fine till today. Today I had this error:
convert: Empty input file '/temp/img-file.jpg'
which again made me think that next line (converting image size) is executed before file is downloaded from bucket to temp folder.
Am I missing something ?
Apparently I've made some errors due to fact that there are still many libraries out there not having proper type definitions, or not at all, including Google ones, and me making wrong guesses.
Be careful using libraries without Type Definitions and open their source to see return types.
While searching a solution I ran into this ansfer as well: GCS permissions related error and a bash script to fix. If you read this answer maybe your problem is related to that, so I share it here too.
UPDATE:
Again I've seen an error, so to double make sure I've included both async calls in try/catch:
let myBucket : any = {};
try {
myBucket = await googlecloudstorage.bucket('<bucket-id>');
await myBucket.file(path).download({
destination: tempFilePath
})
} catch (error) {
console.log(error);
}
UPDATE 2:
Still having errors, trying o figure it out within Typescript but I plan to go back to plain javascript. Also I've tried updating ACLs with a batch file as explained in issue I've provided link above, didn't seem to help.
UPDATE 3 (Hopeful to be last):
Well, for calls related to Google Cloud Storage I use plain javascript promises now and ditch async/await, like ones used in Firebase-Functions Samples repo, so far so good.
UPDATE 4:
I kept getting errors, after changing my code, upon uploads/reads inconsistently. Made further searches and...
Read this answer from another question describing same problem. Basically it's not us, it's them, but they don't admit. It's because the way google-cloud-node package uses sockets.
Will post it here if some sane way of fixing this comes up.
UPDATE 5:
Here is how not to keep sockets open forever:
var gcs = require('#google-cloud/storage')(...)
gcs.interceptors.push({
request: function(reqOpts) {
reqOpts.forever = false
return reqOpts
}
})
...seems to work, for now.
I am using r.js with uglify to minify and concatenate my scripts. I am getting some errors on a production site where the stack trace returned is unintelligible. I would like to temporarily turn off the mangling of function names (variable names are fine) and am having trouble working out how to do this as r.js wraps the configuration options that are passed to uglify.js
The uglify config section in my r,js build config looks like this
uglify: {
beautify: true,
indent_start: 0,
indent_level: 1,
}
I would like to add the
-nmf or --no-mangle-functions – in case you want to mangle variable names, but not touch function names. (from here)
If i add the line
uglify: {
beautify: true,
indent_start: 0,
indent_level: 1,
'--no-mangle-functions': true
}
It does nothing, as does 'no-mangle-functions': true.
How do I pass this option to uglify?
Trying to make uglified/mangled code readable kind of defeats it's purpose in the first place.
Probably, what you are after are source maps.
To generate source maps in Uglify just add these options:
uglify: {
options: {
sourceMap: 'build.min.map',
sourceMappingURL: 'build.min.map'
}
}
Map filename must mirror the final javascript filename:
uglified.js <=> uglified.map
From what i can see in the source code of r.js there is no direct differentiation between functions and variable names. But there is an option called no_functions which is actually passed to the uglify section where the default value is false
Passing of options:
https://github.com/jrburke/r.js/blob/master/dist/r.js#L25067
Defaulting no_functionsto false:
https://github.com/jrburke/r.js/blob/master/dist/r.js#L11492
I cannot test it right now, so i am only guessing. Maybe you can try this option