-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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(cli): auto-generate index.ts for exports #1363
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.
👏👏👏 Small QoL comments
@@ -235,6 +235,7 @@ module.exports = class ControllerGenerator extends ArtifactGenerator { | |||
'Controller %s is now created in src/controllers/', | |||
this.artifactInfo.name, | |||
); | |||
super.updateIndexFile(); |
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 returned since it's a promise? It might not matter since it's the last thing being done here, but I think it makes sense to return it for the sake of using a promise
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.
Also, shouldn't updateIndexFile
be called in ArtifactGenerator
since most artifacts seem to use index.ts
?
packages/cli/lib/make-index.js
Outdated
const outFile = `${dir}/index.ts`; | ||
debug(`outFile => ${outFile}`); | ||
|
||
const writeFileAsync = util.promisify(fs.writeFile); |
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 reason as to why we don't use fs.writeFileSync
?
|
||
async updateIndexFile() { | ||
await index(this.artifactInfo.outDir, { | ||
prefix: `.${this.artifactInfo.type}`, |
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.
It's weird to me to have to .
as part of prefix
. I think it's better for .
to be inferred when setting globPattern
in make-index.js
19ee9fb
to
6932169
Compare
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 have few high-level concerns/comments:
(1) Is there a reasonably-easy way how to test make-index
?
(2) Is it a good idea to force index.ts
file to be auto-generated? What if app developers decide to add more files, perhaps in a deeper directory structure, and want to export only subset of entities from those additional files?
Instead of generating the whole file, can we simply append new export * from
lines at the bottom?
// Copyright IBM Corp. 2017,2018. All Rights Reserved. | ||
// Node module: @loopback/example-todo | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT |
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.
Shouldn't the example files preserve license headers? (I understand the files generated by lb4
do not come with license headers.)
packages/cli/.prettierrc
Outdated
@@ -1,6 +0,0 @@ | |||
{ | |||
"bracketSpacing": 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.
What is the reason for removing .prettierrc
from this project? I am not against such change per se, but would like to better understand the reasoning. It may be better to make this change in a standalone pull request.
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.
The reason was that this .prettierrc
uses different formatting that the top-level one we use for the project and I noticed that and removed it. I've reverted this change and will make a new PR for this.
packages/cli/lib/format.js
Outdated
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
const prettier = require('prettier'); |
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.
Cross-posting item 8 from #441 (comment):
Formatting of generated code: it would be great if our tooling was able to pick up prettier version and configuration from the project we are running in and format the generated code accordingly.
Imagine a user is using prettier
in their project (as scaffolded by lb4 app
), but changed prettier config to use double-quotes and tabs. If we don't honor their style, they will have to run npm run lint:fix
every time they add a new model or update auto-generated TypeScript definitions.
It is also important to run prettier
from node_modules
of the target project, because different prettier version have variations in how they format the same piece of code and we cannot get all users to have exactly the same version of prettier as we use in LB4 tooling.
It may be enough to add ${this.destinationPath()}/node_modules
to the list of directories where Node.js looks for modules. See how Mocha handles that (link):
module.paths.push(cwd, join(cwd, 'node_modules'));
I am ok to leave these two improvements out of scope of this initial pull request as long as there is a follow-up issue (or two?) to ensure we fix this problem as part of the DP3 Boot epic.
packages/cli/lib/make-index.js
Outdated
nested: true, | ||
prefix: '', | ||
commentHeader: `This is an auto-generated file. DO NOT EDIT.`, | ||
commentFooter: `This is an auto-generated file. DO NOT EDIT.`, |
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 particular reason for using backticks (template literals) instead of regular single-quotes?
packages/cli/lib/make-index.js
Outdated
files.forEach(file => { | ||
const relPath = `export * from './${path | ||
.relative(dir, file) | ||
.slice(0, -3)}';`; |
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.
Please extract the expression computing the actual path outside of the template literal and save the result to a variable.
const relativePath = path.relative(dir, file).slice(0, -3);
const exportLine = `export * from '${relativePath}';`;
lines.push(relPath);
packages/cli/lib/make-index.js
Outdated
debug(`outFile => ${outFile}`); | ||
|
||
const writeFileAsync = util.promisify(fs.writeFile); | ||
await writeFileAsync(outFile, code); |
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 will break incremental builds, because every run of make-index
is going to change the modification time of index.ts
to the current date, even in cases where the file content was not changed at all.
Proposed solution:
- Read the current file content and if the old content is the same as the new content, then don't write anything. I believe this is what prettier is doing too.
- Set
mtime
of the written file to match the latestmtime
of files we were generating index from. I.e. for each file indir
(excludingindex.ts
), obtainmtime
value and then pick the maximum value of all mtimes.
I think the second item is not enough on its own, because file watchers like tsc --watch
, fs-events, etc. listen for write events and ignore mtime
.
The first item may is most likely good enough, because typically the index.ts
file would be updated shortly after the last non-index file was added/updated.
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 am ok to leave these two improvements out of scope of this initial pull request as long as there is a follow-up issue to ensure we fix this problem as part of the DP3 Boot epic.
packages/cli/lib/make-index.js
Outdated
const comments = comment.split('\n'); | ||
let normalizedComment = ''; | ||
|
||
// For each comment line -- if it's a comment we |
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 it's a comment we
We do what? Looks like you accidentally forgot to finish the sentence here.
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.
Looks good at high-level. Couple of thoughts
@@ -0,0 +1 @@ | |||
export * from './ping.controller'; |
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 makes me wonder whether it should be included when the app is generated as opposed to adding it on when the controller generator is run. Thoughts?
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.
Check the file -- It's in the app
generator and this is just the default index.ts
that gets generated since we are generating ping.controller
in new apps.
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.
The ping controller I know about, but I was wondering about whether we should include this index.ts
file when the app generator is run
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.
Yea when the app generator is run there will be a src/controllers/index.ts
that will be generated which exports the ping controller.
const path = require('path'); | ||
const util = require('util'); | ||
const fs = require('fs'); | ||
const appendFileAsync = util.promisify(fs.appendFile); |
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 use this instead? https://nodejs.org/api/fs.html#fs_fs_appendfilesync_path_data_options
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.
appendFileSync
is a blocking / synchronous operation. async / await
is a promise under the hood even though it appears to be synchronous when written.
Async/await makes asynchronous code look and behave a little more like synchronous code. This is where all its power lies.
In a generator you can call updateIndex
without an await
... and have more code below -- and that additional code and continue running without being blocked essentially.
Here's a Node.js article on blocking / non-blocking. https://nodejs.org/en/docs/guides/blocking-vs-non-blocking/
throw new Error(`${file} must be a TypeScript (.ts) file`); | ||
} | ||
const content = `export * from './${file.slice(0, -3)}';\n`; | ||
await appendFileAsync(indexFile, content); |
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 we explicitly check whether the line exists already? Or is it that too much of an edge case
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.
Maybe. My assumption is to let the generator handle that -- within a generator this function is triggered only when the generationStatus
is not an error / identical
file. So if the file exists this won't be called. -- That said another check doesn't hurt. 🤷♂️
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.
Nvm, I forgot about yeoman's own conflicter system
const expectedFile = path.join(SANDBOX_PATH, 'index.ts'); | ||
|
||
describe('update-index unit tests', () => { | ||
beforeEach('reset sandbox', () => sandbox.reset()); |
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 it possible to just provide sandbox.reset
since it isn't async anymore?
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've tried and I get the following error message if I do beforeEach(sandbox.reset)
:
1) update-index unit tests
"before each" hook: reset for "creates index.ts when not present":
TypeError: this.validateInst is not a function
at Context.reset (packages/testlab/src/test-sandbox.ts:31:27)
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.
Looks like it has to do with scoping :(.
On the other hand, do you think async () => await sandbox.reset()
is cleaner than () => sandbox.reset())
? I feel we might as well use it.
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 like the current format -- it's how we use sandbox.reset
in the beforeEach hooks of other tests. Consistency is good IMO
}); | ||
|
||
it('throws an error when given a non-ts file', async () => { | ||
expect(updateIndex(SANDBOX_PATH, 'test.js')).to.be.rejectedWith( |
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.
needs to be wrapped in an anonymous function
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?
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 think I was thinking of error assertions. pls ignore
fix #1127 Signed-off-by: Taranveer Virk <[email protected]>
This is part of the work I did for the Boot Spike.
lb4 datasource
makes use of the index generation so decided to just deliver this as it's own feature.fix #1127
UPDATE: MAY 31
Checklist
npm test
passes on your machinepackages/cli
were updatedexamples/*
were updated