How to remove un used object literal using `ts-morph`? - typescript-compiler-api

I have a language file which exports the texts as object literal,
export const lang = {
hello : `hello`,
calculate : (value = 0) => `the value is ${value}`,
nested: {
value: 'nested value'
}
}
This constant is imported by many different files in the project. I am trying to find the properties that are un used and remove them.
const project = new Project({ tsConfigFilePath: 'tsconfig.webpack.json' });
const source = project.addSourceFileAtPathIfExists('constants.ts');
const messages = source?.getVariableDeclarationOrThrow('lang');
const value = messages?.getInitializerOrThrow();
const properties = (value as ObjectLiteralExpression)?.getProperties() ?? [];
// I can iterate each property and their child but how to check if they are unused??
Is there any way to know that a property is un used and remove it?

Related

How to get the next sibling and ignore tokens in ts-morph?

I try to get the next sibling using getNextSibling from ts-morph.
The problem is I get the node DotToken or OpenBracketToken which I don't care about them. I want the node with the content. the problem this is can be any node (Identifier or StringLiteral or BinaryExpression or anything else).
Is there a safe way to get the node and ignore the tokens?
import { Identifier, Project, SyntaxKind } from "ts-morph";
console.clear();
const project = new Project();
const sourceFile = project.createSourceFile(
"foo.ts",
`
foo.bar.some();
foo[bar].some();
foo['bar'].some();
foo['bar' + 'other'].some();
`
);
const foos = sourceFile
.getDescendantsOfKind(SyntaxKind.Identifier)
.filter((i) => i.getText() === "foo");
foos.forEach((foo) => {
const s = foo.getNextSibling();
console.log({ s: s.getText() }); // outputs: . [ [ [
});
codesandbox.io
ts-ast-viewer.com

Structure of object fetched to firebase from GAS Google sheet

I have script tied to Google sheet that collects data from 3 columns (date, userID, value) on the sheet and pass them to object that is then fetched to Firebase DB.
The script correctly push the object into Firebase but I have troubles to achieve structure of data object that I need (so the nodes are correctly structured in Firebase). In the “date” column in the sheet I have date under which I want to group userID:value pair but when I iterate over rows in the sheet it only stores last userID:value pair with given date and not all.
My code:
function writeValues() {
// Database reference
const databaseURL = "https://sampledatabaseURL.firebaseio.com/"
const token = ScriptApp.getOAuthToken();
// Get spreadsheet, range of data and all values within
var spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadSheet.getSheetByName("test");
const range = sheet.getDataRange();
const allValues = range.getValues();
// Get indexes for headers in columns
const headers = {};
allValues[0].forEach(function (value, index) {
headers[value] = index
});
const dbData = {};
allValues.forEach(function (row, index) {
if (index === 0) { return } // skip header row
dbData[row[headers.date]] = {
[row[headers.userID]] : row[headers.value]
}
})
const url = databaseURL + "/test.json?access_token=" + encodeURIComponent(token)
const response = UrlFetchApp.fetch(url, {
method: 'put',
payload: JSON.stringify(dbData)
})
Logger.log(response.getResponseCode());
}
Output JSON file it produces and that’s fetched to Firebase:
{
"20180118" : {"userID2" : "value2"},
"20200705" : {"userID4" : "value4"},
"20210324" : {"userID6" : "value6"}
}
Desired output structure:
{
"20180118" : {"userID1" : "value1", "userID2" : "value2"},
"20200705" : {"userID3" : "value3", "userID4" : "value4"},
"20210324" : {"userID5" : "value5", "userID6" : "value6"}
}
I believe your goal as follows.
You want to achieve the following situation.
From: This image is from your question.
To: You want to retrieve the following output as the value of dbData.
{
"20180118" : {"userID1" : "value1", "userID2" : "value2"},
"20200705" : {"userID3" : "value3", "userID4" : "value4"},
"20210324" : {"userID5" : "value5", "userID6" : "value6"}
}
In this case, how about the following modidfication? When your script is modified, it becomes as follows.
From:
const allValues = range.getValues();
// Get indexes for headers in columns
const headers = {};
allValues[0].forEach(function (value, index) {
headers[value] = index
});
const dbData = {};
allValues.forEach(function (row, index) {
if (index === 0) { return } // skip header row
dbData[row[headers.date]] = {
[row[headers.userID]] : row[headers.value]
}
})
To:
const [, ...values] = range.getValues();
const dbData = values.reduce((o, [a,b,c]) => {
if (!o[a]) o[a] = {};
o[a][b] = c;
return o;
}, {});
console.log(dbData)
When above modified script is run for your sample Spreadsheet, the value of dbData is the output you expect.
Reference:
reduce()

Typescript transformer, `node.parent` is undefined

I'm currently using a typescript transformer api, and I found that the node.parent is undefined.
My code is:
const transformerFactory: ts.TransformerFactory<ts.Node> = (
context: ts.TransformationContext
) => {
return (rootNode) => {
function visit(node: ts.Node): ts.Node {
node = ts.visitEachChild(node, visit, context);
// HERE node.parent IS UNDEFINED !
return filterFn(node, context);
}
return ts.visitNode(rootNode, visit);
};
};
const transformationResult = ts.transform(
sourceFile, [transformerFactory]
);
How can I find the parent of the node?
You can parse specifying to set the parent nodes:
const sourceFile = ts.createSourceFile(
"fileName.ts",
"class Test {}",
ts.ScriptTarget.Latest,
/* setParentNodes */ true, // specify this as true
);
Or do some operation on the node to get it to set its parent nodes (ex. type check the program... IIRC during binding it ensures the parent nodes are set).
Update based on comment
If you are creating these from a program, then you can do the following:
const options: ts.CompilerOptions = { allowJs: true };
const compilerHost = ts.createCompilerHost(options, /* setParentNodes */ true);
const program = ts.createProgram([this.filePath], options, compilerHost);

AngularFire2 - Append to list child

I'm trying to add an object to /users/[userKey]/invitedTo but set deletes the existing data, so does update.
What I want to end up with is something like this:
users
-uniqueuserkey
--name: Name
--InvitedTo
---eventuniquekey1
----eventname: event name 1
----etc
---eventuniquekey2
----eventname: event name 2
----etc
-
// this.event.push(eventObj);
this.event.push(eventObj).then((item) => {
if (item) {
const itemKey = item.key;
for (const key in guests) {
if (guests.hasOwnProperty(key)) {
const invitedObj = {};
const invitedTo = this.db.object(`/users/${key}/invitedTo`);
invitedObj[itemKey] = eventObj;
invitedTo.set( { invitedObj } );
}
}
}
});
Update does exactly what I need, but it also deletes existing value:
for (const key in guests) {
if (guests.hasOwnProperty(key)) {
const invitedObj = {};
invitedObj[itemKey] = eventObj;
this.users.update(key, { invitedTo: invitedObj });
}
}
Should I just get the existing data and add to it?
If you want to add an object, you (usually) should use the push method instead of set or update:
const invitedTo = this.db.list(`/users/${key}/invitedTo`);
invitedTo.push(eventObj);
That way, Firebase will create a unique key and add it to the invitedTo node.
If you want to set the key yourself, then you could use the update method like this:
const invitedTo = this.db.object(`/users/${key}/invitedTo/${itemKey}`);
invitedTo.update(eventObj);

How can I dynamic set a value into a property?

How to set a property to value that should be resolve.. like this one..
const getDataFromServer = (id) => ({id: * 2})
R.set(payloadProp, getDataFromServer)({id: 4}); // WRONG, is setting to a function instend to resolve a function with `{id: 4}`
const fetch = (id) => {
return { num: id * 2 }
};
const idProp = R.lensProp('id');
const getDataFromServer = R.pipe(R.view(idProp), fetch);
const payloadProp = R.lensProp('payload');
const setPayloadFromFetch = R.set(payloadProp, getDataFromServer); // NOT WORK, return payload as function
const obj = { id: 1, payload: { message: 'request' } }
const ret = setPayloadFromFetch(obj);
console.log(ret);
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.23.0/ramda.min.js"></script>
The problem is that R.set takes a value, not a function, for its second parameter. And you can't switch to R.over, which does take function but calls it with the current value at that lens, not the full data object supplied to the outer function.
The simplest way to solve this is simply to pass the object in both places:
const setPayloadFromFetch = obj => R.set(payloadProp, getDataFromServer(obj), obj);
But if you're intent on making this point-free, lift is your friend:
const setPayloadFromFetch = R.lift(R.set(payloadProp))(getDataFromServer, identity);
although you could also use R.ap, which would be very nice except that R.set takes its parameters in the wrong order for it, and so you have to use R.flip
const setPayloadFromFetch = R.ap(R.flip(R.set(payloadProp)), getDataFromServer);
You can see all these in the Ramda REPL.

Resources