Skip to content
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

[FEATURE] Add generateResourcesJson task #390

Merged
merged 88 commits into from
Aug 24, 2020
Merged

Conversation

codeworrior
Copy link
Member

@codeworrior codeworrior commented Dec 29, 2019

Introduces new task "generateResourcesJson" which creates the file resources.json for libraries, applications and themes.
The task analyses the resources and collects meta information and stores them in a file called resources.json.
This meta information includes module name, size, dependencies, includes, ...

@coveralls
Copy link

coveralls commented Dec 29, 2019

Coverage Status

Coverage increased (+0.7%) to 92.279% when pulling a58c11e on create-resources-json into c47c8c6 on master.

@codeworrior codeworrior force-pushed the create-resources-json branch 2 times, most recently from 19ac264 to dcb2252 Compare December 29, 2019 18:18
@codeworrior
Copy link
Member Author

Note: failing test requires #391.


getProject() {
return this.resource._project;
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method has been added in the moduleBundler, but I couldn't find a local usage within ui5-builder. However, code-coverage says, it is used. Do you know where?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is not called! Will remove it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  tasks › bundlers › generateStandaloneAppBundle.integration › integration: build application.b standalone

  Rejected promise returned by test. Reason:

  TypeError {
    message: 'resource.getProject is not a function',
  }

  › module.exports (lib/lbt/utils/escapePropertiesFile.js:14:27)
  › BundleBuilder.writePreloadModule (lib/lbt/bundle/Builder.js:432:30)
  › BundleBuilder.writePreloadFunction (lib/lbt/bundle/Builder.js:288:17)
  › BundleBuilder._createBundle (lib/lbt/bundle/Builder.js:151:4)
  › Object.module.exports [as task] (lib/tasks/bundlers/generateStandaloneAppBundle.js:84:2)
  › Object.build (lib/builder/builder.js:368:4)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the original one gets called, but the copied one didn't

return path.slice( "/resources/".length);
}

class LocatorResource extends Resource {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we have best practices how to share code between processors? They so far seem to be self-contained? But I need the LBT pool now in the resourceListCreator, too. I copied the LocatorResource and LocatorResourcePool for now, but that's not really nice.

Copy link
Contributor

@tobiasso85 tobiasso85 Jun 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resourceListCreator and moduleBundler use it.

How about a subfolder in processors -> processors/lib for helpers and shared code between processors?
Note: This was done the same way for jsdoc -> processors/jsdoc/lib

resources.push(res);
}
});
} else {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This felt strange to me. Is there no easier (and official) way to get a list of all resources of the current project? workspace.byGlobSource only returns source files, no generated files, a search with the namespace pattern doesn't work for sap.ui.core or for apps without a namespace.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

workspace.byGlob also only returns resources of the current project. Searching with the namespace should be possible by now (I know your comment is from last year), since with v2.0 all projects are required to have a namespace.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And what about sap.ui.core? Yet another, hard-coded exception?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, now I get it. Indeed for sap.ui.core the namespace sap/ui/core is insufficient. A glob pattern like to current one should be fine though.

Still, why do we use byGlobSource instead of byGlob? I thought we are also interested in generated files.

Copy link
Contributor

@tobiasso85 tobiasso85 Aug 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

byGlobSource retrieves the debug files as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

byGlobSource retrieves the debug files as well.

Are you sure? I think that should not be the case unless they already exist in the source directory.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're right, byGlob retrieves all the files.

adjusted logic for copy from source such that it does not depend on the combination of byGlobSource and adding missing ones from byGlob on top.
f346f3e

}
// a .theme file within a theme folder indicates a library/theme package
// Note: ignores .theme files in library folders
if ( name.match("(?:[^/]+/)*themes/[^/]+/(?:\\.theming|library.source.less)") && !this._themePackages.has(prefix) ) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the Maven tooling, the library.source.less was not used as a marker, but the ui5-tooling does not create the expected .theming files. @matz3 What is the right approach here? Should we create the .theming files or chose a different approach?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to create the files in order to support the Theming Engine. This becomes relevant when our main delivery is built with the UI5 Tooling.

For now the library.source.less as an indicator is IMO fine. We also use it within the theme build as the files are required for the build (which was not the case in the legacy Maven build).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we generate now .theming files, we could omit the library.source.less, if and only if the task execution order is appropriate.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only do so in the internal theme build (which produces the current delivery for SAPUI5 / OpenUI5 (except the npm packages).
So I think we need to stay with library.source.less for the time being.

if ( options.failOnOrphans ) {
throw new Error("not all resources could be assigned to components");
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a guideline where errors should be thrown, processors or tasks?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the lowest level is here perfect because we have all the information there and typically the error should bubble all the way up

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine, but the error message should try to explain the context better. The call stack might get lost through catching and wrapping this error while bubbling up.

Some good examples:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

"!**/.eslintrc",
"!**/.eslintignore",
"!**/.gitignore"
];
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a common place for such excludes?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not yet. Although I would not expect the last three in the webapp or src directories. Do we know of such cases?

Would it be possible to rather have an include on the expected file extensions?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the file extensions can be anything required by the application, from image to web resource, therefore it is hard to define a whitelist.

@tobiasso85 tobiasso85 force-pushed the create-resources-json branch from f72c914 to df0fb0b Compare June 22, 2020 13:12
@@ -113,6 +124,21 @@ class ResourceInfo {
if ( this.included != null && this.included.size > 0 ) {
this.merged = true;
}
if (orig.size > 0) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as the default now is -1, shouldn't the check be for orig.size >= 0 ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point :) just found it in the original sources.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, there it's orig.size >= 0, too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

return Promise.all(
resources.map(
(resource) => this.addResource( new LocatorResource(this, resource) )
).filter( (followUp) => followUp )
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just Boolean

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

(resource) => this.addResource( new LocatorResource(this, resource) )
).filter( (followUp) => followUp )
);
// .then( () => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented code can be deleted...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
// a .theme file within a theme folder indicates a library/theme package
// Note: ignores .theme files in library folders
if ( name.match("(?:[^/]+/)*themes/[^/]+/(?:\\.theming|library.source.less)") && !this._themePackages.has(prefix) ) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we generate now .theming files, we could omit the library.source.less, if and only if the task execution order is appropriate.

} catch (e) {
// ignore error
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this the most expensive way to determine the size of the resource?

getString() has to read the resource, convert the UTF-8 byte stream to a Unicode16 char sequence; Buffer.byteLength then has to do the opposite operation just to determine the original UTF-8 byte length.

Overall performance when building sap.ui.core also looks very bad.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm already counted the buffer length but that's not correct, have to find a better way.
The statInfo would be the best, but this needs to be updated when the sources are compressed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why / how was the buffer length not correct?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, Buffer.prototype.length is the allocation size, not the content length. Too bad. To me that means, @ui5/fs.Resource needs to know the size, if neither statInfo nor Buffer are suitable to determine it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe for
@ui5/fs.Resource
we can use the stats.size property for size
but we must ensure that each content modification will trigger a size update

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly, maintaining the (byte) size IMO should become a responsibility of the Resource, to get a proper abstraction.

Downside: when new content is provided as a string, this unfortunately requires either encoding the string as UTF-8 or using Buffer.byteLength. Maybe that update of the size then should be done lazily.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree the Resource should be responsible.
If a string is provided it is anyways converted to a buffer and the buffer.byteLength is just a property so there should be no performance impact.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can use statinfo size with
SAP/ui5-fs#253

resources = await workspace.byGlob(["/resources/**/*.*", ...DEFAULT_EXCLUDES]);
}
const dependencyResources = await dependencies.byGlob("/resources/**/*.{js,json,xml,html,properties,library}");

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea, why I added the resources from dependencies in my initial commit.

Thinking about it now, the resources.json is a library-local thing (or component-local thing) and should not include the dependencies. They have their own resources.json.

Wanted to verify the impact by building sap.ui.layout with dependencies. But building it failed in sap.ui.core, because sap.ui.core has modules outside its namespace -> orphans.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done with d97bbe8

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: Analysis of XMLViews and fragments requires the resources from dependencies. That was one reason t odd them.

@tobiasso85 tobiasso85 force-pushed the create-resources-json branch from 815b826 to ea2ae76 Compare July 29, 2020 12:37
resources.push(res);
}
});
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, now I get it. Indeed for sap.ui.core the namespace sap/ui/core is insufficient. A glob pattern like to current one should be fine though.

Still, why do we use byGlobSource instead of byGlob? I thought we are also interested in generated files.

"!**/.eslintrc",
"!**/.eslintignore",
"!**/.gitignore"
];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not yet. Although I would not expect the last three in the webapp or src directories. Do we know of such cases?

Would it be possible to rather have an include on the expected file extensions?

package.json Outdated
@@ -21,7 +21,7 @@
"test": "npm run lint && npm run jsdoc-generate && npm run coverage",
"test-azure": "npm run lint && npm run jsdoc-generate && npm run coverage-xunit",
"lint": "eslint ./",
"unit": "rimraf test/tmp && ava",
"unit": "rimraf test/tmp && ava -v",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what the unit-verbose npm script one line below is supposed for. Or was this changed for some temporary test?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was just temporary, this change somehow slided in, will revert this line

if ( options.failOnOrphans ) {
throw new Error("not all resources could be assigned to components");
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine, but the error message should try to explain the context better. The call stack might get lost through catching and wrapping this error while bubbling up.

Some good examples:

@tobiasso85 tobiasso85 force-pushed the create-resources-json branch from f2ed108 to f73a050 Compare August 4, 2020 08:10
@tobiasso85 tobiasso85 changed the title [FEATURE][WIP] Generate resources.json files for libs, components, themes [FEATURE] Generate resources.json files for libs, components, themes Aug 4, 2020
@tobiasso85 tobiasso85 requested a review from RandomByte August 7, 2020 14:28
@RandomByte
Copy link
Member

I think I won't be able to review the functional correctness of this PR in coming week. I simply don't know enough about what this task does and should do.

I'm surprised by the amount of new test fixtures. But for the same reason as above I can't tell whether they are needed or not.

But I tried something that might be interesting: I removed the new task execution from ApplicationBuilder.js, LibraryBuilder.js and ThemeLibraryBuilder.js. With that change, the code coverage for ResourceCollector.js, ResourceInfo.js and resourceListCreator.js went from almost 100% to below 70% each. This gives me the impression that some of the new functions are only tested through integration tests and do not have dedicated unit tests:

Screenshot 2020-08-07 at 18 49 56

For new modules we typically try to have at least one unit test per function. First, to efficiently test a variety of inputs. And secondly, to have failing tests which already contain the name of faulty function instead of a more generic integration test which first needs to be debugged on its own to identify the issue.

@RandomByte
Copy link
Member

RandomByte commented Aug 8, 2020

Building the openui5-sample-app with this change currently fails with an error:

⚠️  Process Failed With Error

Error Message:
resources.json generation failed with error: There are 1 resources which could not be assigned to components.

Stack Trace:
Error: resources.json generation failed with error: There are 1 resources which could not be assigned to components.
    at module.exports (/ui5-builder/lib/processors/resourceListCreator.js:183:9)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at async Object.build (/ui5-builder/lib/builder/builder.js:370:4)
    at async Object.handleBuild [as handler] (/ui5-cli/lib/cli/commands/build.js:97:2)

The errors logged during the build are:

ERR! lbt:resources:ResourcePool TypeError: Cannot read property 'type' of undefined
ERR! lbt:resources:ResourcePool     at new SapUiDefineCall (/ui5-builder/lib/lbt/calls/SapUiDefine.js:34:16)
ERR! lbt:resources:ResourcePool     at XMLCompositeAnalyzer.analyze (/ui5-builder/lib/lbt/analyzer/XMLCompositeAnalyzer.js:19:23)
ERR! lbt:resources:ResourcePool     at determineDependencyInfo (/ui5-builder/lib/lbt/resources/ResourcePool.js:71:35)
ERR! lbt:resources:ResourcePool     at processTicksAndRejections (internal/process/task_queues.js:93:5)
ERR! lbt:resources:ResourcePool     at async LocatorResourcePool.getModuleInfo (/ui5-builder/lib/lbt/resources/ResourcePool.js:175:11)
ERR! lbt:resources:ResourcePool     at async Promise.all (index 9)
ERR! lbt:resources:ResourcePool     at async module.exports (/ui5-builder/lib/processors/resourceListCreator.js:117:2)
ERR! lbt:resources:ResourcePool     at async Object.build (/ui5-builder/lib/builder/builder.js:370:4)
ERR! lbt:resources:ResourcePool     at async Object.handleBuild [as handler] (/ui5-cli/lib/cli/commands/build.js:97:2)
ERR! lbt:resources:ResourcePool  failed to parse or analyze %s: sap/ui/demo/todo/test/unit/AllTests.js TypeError: Cannot read property 'type' of undefined
ERR! lbt:resources:ResourcePool     at new SapUiDefineCall (/ui5-builder/lib/lbt/calls/SapUiDefine.js:34:16)
ERR! lbt:resources:ResourcePool     at XMLCompositeAnalyzer.analyze (/ui5-builder/lib/lbt/analyzer/XMLCompositeAnalyzer.js:19:23)
ERR! lbt:resources:ResourcePool     at determineDependencyInfo (/ui5-builder/lib/lbt/resources/ResourcePool.js:71:35)
ERR! lbt:resources:ResourcePool     at processTicksAndRejections (internal/process/task_queues.js:93:5)
ERR! lbt:resources:ResourcePool     at async LocatorResourcePool.getModuleInfo (/ui5-builder/lib/lbt/resources/ResourcePool.js:175:11)
ERR! lbt:resources:ResourcePool     at async Promise.all (index 9)
ERR! lbt:resources:ResourcePool     at async module.exports (/ui5-builder/lib/processors/resourceListCreator.js:117:2)
ERR! lbt:resources:ResourcePool     at async Object.build (/ui5-builder/lib/builder/builder.js:370:4)
ERR! lbt:resources:ResourcePool     at async Object.handleBuild [as handler] (/ui5-cli/lib/cli/commands/build.js:97:2)
ERR! lbt:resources:ResourcePool TypeError: Cannot read property 'type' of undefined
ERR! lbt:resources:ResourcePool     at new SapUiDefineCall (/ui5-builder/lib/lbt/calls/SapUiDefine.js:34:16)
ERR! lbt:resources:ResourcePool     at XMLCompositeAnalyzer.analyze (/ui5-builder/lib/lbt/analyzer/XMLCompositeAnalyzer.js:19:23)
ERR! lbt:resources:ResourcePool     at determineDependencyInfo (/ui5-builder/lib/lbt/resources/ResourcePool.js:71:35)
ERR! lbt:resources:ResourcePool     at processTicksAndRejections (internal/process/task_queues.js:93:5)
ERR! lbt:resources:ResourcePool     at async LocatorResourcePool.getModuleInfo (/ui5-builder/lib/lbt/resources/ResourcePool.js:175:11)
ERR! lbt:resources:ResourcePool     at async Promise.all (index 23)
ERR! lbt:resources:ResourcePool     at async module.exports (/ui5-builder/lib/processors/resourceListCreator.js:117:2)
ERR! lbt:resources:ResourcePool     at async Object.build (/ui5-builder/lib/builder/builder.js:370:4)
ERR! lbt:resources:ResourcePool     at async Object.handleBuild [as handler] (/ui5-cli/lib/cli/commands/build.js:97:2)

@RandomByte
Copy link
Member

I also did some benchmarking using:

OpenUI5 sap.m library

ui5-builder ref Command Mean [s] Min [s] Max [s]
master (e7d4431) ui5 build 24.354 ± 2.094 21.244 26.661
create-resources-json (18fb726) ui5 build 27.820 ± 1.174 26.073 30.163

OpenUI5 Sample App (+ 6 dependencies)

(skipping the generateResourcesJson task for applications due to the error above)

ui5-builder ref Command Mean [s] Min [s] Max [s]
master (e7d4431) ui5 build --all 65.268 ± 2.128 60.574 68.159
create-resources-json (18fb726) ui5 build --all 74.221 ± 1.947 70.642 77.307

Benchmark tool: hyperfine with one warmup
Hardware: MacBook Pro 2017 - i7 2,9GHz/16GB

@codeworrior
Copy link
Member Author

The error seen during the build of the openui5-sample-app should be fixed with #491.

@RandomByte
Copy link
Member

@codeworrior and I discussed the benchmark results:

  • The resources.json is not consumed at runtime. Only some known tools make use of the file.
    • This means many UI5 Tooling consumer do not need it
  • Both measurements show an increase in runtime of about 14%.
  • This task might fail for some projects that are currently working fine
    • For example a library with multiple namespaces would need a way to configure those (currently not possible). Otherwise the build will likely fail

Both, @codeworrior and I therefore think it would be better to skip this task by default, similar to the generateManifestBundle task. For this it needs to be added to the list here: https://github.com/SAP/ui5-builder/blob/master/lib/builder/builder.js#L54

Btw. should we document the structure of the resources.json somewhere? Do we expect other tools to make use of it? I'm not sure.

@@ -277,6 +277,9 @@ test("Build application.g", (t) => {
const expectedPath = path.join("test", "expected", "build", "application.g", "dest");

return builder.build({
includedTasks: [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any integration test left that does not execute the generateResourceJson task now?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

distributed it more

@@ -86,14 +94,14 @@ test("Build application.a", (t) => {
return builder.build({
tree: applicationATree,
destPath,
excludedTasks: ["generateComponentPreload", "generateStandaloneAppBundle", "generateVersionInfo"]
excludedTasks: ["generateComponentPreload", "generateStandaloneAppBundle", "generateVersionInfo", "generateResourcesJson"]
Copy link
Member

@RandomByte RandomByte Aug 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"generateResourcesJson" is not default anymore and therefore does not need to be excluded here and in following tests

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -496,6 +554,9 @@ test("Build theme.j even without an library", (t) => {
const destPath = "./test/tmp/build/theme.j/dest";
const expectedPath = "./test/expected/build/theme.j/dest";
return builder.build({
includedTasks: [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep at least one default theme build

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@tobiasso85 tobiasso85 force-pushed the create-resources-json branch from de267bb to 4c15d27 Compare August 11, 2020 06:44
@codeworrior
Copy link
Member Author

@RandomByte Reg. your comment:

Btw. should we document the structure of the resources.json somewhere? Do we expect other tools to make use of it? I'm not sure.

To be honest, I didn't remember this, but a search in our Wiki revealed an existing documentation in the SDK.

The documented entries had been the "public" ones at the time of writing. But I guess we could enhance the documentation with a few more properties. A JSON schema also could make sense, but low priority as no one would use it currently (and the file is not intended to be maintained manually).

@tobiasso85 tobiasso85 force-pushed the create-resources-json branch from 4b25ac2 to 6cfe7ab Compare August 11, 2020 14:08
}

/**
* Task for creating a resources.json file, describing all productive build resources.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a link to the relevant documentation: https://openui5.hana.ondemand.com/#topic/adcbcf8b50924556ab3f321fcd9353ea ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

sap.ui.requireSync("./changeHandler/SplitButton");
oRm.openStart("button", oButton);
oRm.class("libNBtnBase");
oRm.openEnd();
Copy link
Member

@RandomByte RandomByte Aug 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is there so much code in some of these fixtures? How is this relevant to the bundling/resources.json generation?

It's seemingly copied from somewhere. But I couldn't find it in the OpenUI5 repository. What's the origin of this code here and in the other new fixtures?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed unnecessary code lines, I couldn't find the origin either.
I think the main purpose was to have dynamic dependencies nested in the ast

@RandomByte
Copy link
Member

RandomByte commented Aug 11, 2020

I cherry-picked the commits from #491 onto this branch and tried building the openui5-sample-app again using ui5 build --include-task=generateResourcesJson --verbose.

It fails with a different error now:

[...]
verb lbt:analyzer:ComponentAnalyzer adding library dependency  sap/m/library.js false
verb lbt:analyzer:ComponentAnalyzer adding library dependency  sap/f/library.js false
verb lbt:analyzer:ComponentAnalyzer adding library dependency  sap/ui/unified/library.js false
verb lbt:analyzer:ComponentAnalyzer derived model implementation dependency  sap/ui/model/resource/ResourceModel.js
verb lbt:analyzer:ComponentAnalyzer derived model implementation dependency  sap/ui/model/json/JSONModel.js
verb lbt:resources:ResourceCollector   configured external resources filters (resources outside the namespace): (none)
verb lbt:resources:ResourceFilterList filetypes: undefined
verb lbt:resources:ResourceFilterList   sap-ui-core-dbg.js --> include: "sap-ui-core-dbg.js"
verb builder:processors:resourceListCreator     writing 'sap/ui/demo/todo/resources.json'
ERR! builder:processors:resourceListCreator resources.json generation failed because of unassigned resources: sap-ui-version.json
ERR! builder:builder Build failed in 523 ms
info builder:builder Executing cleanup tasks...

⚠️  Process Failed With Error

Error Message:
resources.json generation failed with error: There are 1 resources which could not be assigned to components.

Stack Trace:
Error: resources.json generation failed with error: There are 1 resources which could not be assigned to components.
    at module.exports (/ui5-builder/lib/processors/resourceListCreator.js:198:9)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at async Object.module.exports [as task] (/ui5-builder/lib/tasks/generateResourcesJson.js:88:24)
    at async Object.build (/ui5-builder/lib/builder/builder.js:382:4)
    at async Object.handleBuild [as handler] (/ui5-cli/lib/cli/commands/build.js:97:2)

This seems to be related to sap-ui-version.json.

I also don't really like that the ultimate error message is the same as before, even though it's seemingly caused by something else(?)
This will be the error message people will open issues with. Currently it doesn't seem to be very helpful to us.

Edit: I just found that the error log resources.json generation failed because of unassigned resources: sap-ui-version.json was also present in the last case. So maybe #491 actually fixed a different issue? Possibly an issue which actually would not have failed the build?

],
tree: applicationJTree,
destPath,
excludedTasks: ["createDebugFiles", "generateStandaloneAppBundle", "generateVersionInfo"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we would not exclude generateVersionInfo here, this test would reproduce the issue I reported for the openui5-sample-app: #390 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems related to the resource path:
"other resources": /resources/application/j/manifest.json
"sap-ui-version.json": /resources/sap-ui-version.json
--> the prefix /resources/application/j/ is expected by the grouping logic

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sap-ui-version.json was excluded with edbf669
It is not relevant for the resources.json and should not be considered.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will other files with a path like /resources/myfile.xyz still break the build?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes this currently breaks it.
But this is typically the result of a custom task, "normal" resources should reside in /resources/application/j folder

Copy link
Member Author

@codeworrior codeworrior left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stupid Github: as I'm an author, I'm not allowed to "Request changes".

const args = node.arguments;
const nArgs = args.length;
const i = 0;

if ( i < nArgs ) {
if ( isString(args[i]) ) {
const moduleName = ModuleName.fromRequireJSName( args[i].value );
// resolve dependencies absolute: e.g. "library/n/changeHandler/SplitButton.js"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does this change come from? sap.ui.requireSync does not support relative dependencies? Seems wrong to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my bad, didn't know that

sap.ui.requireSync("./Control")
ui5loader-dbg.js:468 Uncaught Error: relative name not supported ('./Control.js'

@@ -404,17 +442,24 @@ class JSModuleAnalyzer {
} else if ( isMethodCall(node, CALL_REQUIRE_SYNC) || isMethodCall(node, CALL_SAP_UI_REQUIRE_SYNC) ) {
// recognizes a call to sap.ui.requireSync
info.setFormat(ModuleFormat.UI5_DEFINE);
onSapUiRequireSync(node, conditional);

onSapUiRequireSync(node, conditional, info.name);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems wrong to me, see below

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done :)
-> above

@@ -0,0 +1,153 @@
const ResourceInfo = require("./ResourceInfo");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module here should rather be named ResourceInfoList, as it otherwise could be confused with a list of Resource.js objects.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done
(was also surprised by the plural "Resources" + "List")

*/
class ResourceCollector {
/**
* Collects resources
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Collects a set of ResourceInfo objects and groups them by components, libraries and themes."

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


const collector = new ResourceCollector(pool);
const visitPromises = resources.map((resource) => {
return resource.getSize().then((size) => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now hat the size became a property of the resource, couldn't the getSize() be integrated in the collector?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point

* @returns {string} new content with resources.json entry
*/
function addResourcesJsonToList(list, prefix) {
// having the file size entry part of the file is a bit like the chicken egg scenario
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find the function name quite misleading. The main purpose of the function seems to be that it creates a JSON string for the given ResourceInfoList. The fact that it also adds the resources.json to the list itself, is an (important) detail on top.

Maybe rather name it makeResourcesJSON....

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

"**/library-all-dbg.js",
"**/designtime/library-preload.designtime.js",
"**/library-preload.support.js"
].join(",")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I had commented on this earlier, but can't find it.

The representation of the filters as comma separated lists is a legacy from the Maven tooling. In a Maven Mojo, those lists have been configured as comma separated lists. But here, in a JavaScript task API, this doesn't make much sense. I would rather switch to pure arrays.

But as long as this is an internal API (as it is currently), it doesn't matter and we could refactor this later.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the rebase -> force push changed the history o0 maybe it was lost/hard to find due to that

@tobiasso85 tobiasso85 force-pushed the create-resources-json branch from e7ade1a to 8b37ebb Compare August 12, 2020 09:01
@tobiasso85 tobiasso85 force-pushed the create-resources-json branch from 33223ed to c8f3a80 Compare August 19, 2020 08:07
@@ -48,7 +48,7 @@ function getBundleDefinition(namespace) {

// include only thirdparty that is very likely to be used
"sap/ui/thirdparty/crossroads.js",
"sap/ui/thirdparty/caja-htmlsanitizer.js",
"sap/ui/thirdparty/caja-html-sanitizer.js",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not related to the resources.json task, right? I would prefer to fix this in a separate PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done with 7d41128

*
* @type {RegExp}
*/
const copyrightCommentsAndBundleCommentPattern = /copyright|\(c\)(?:[0-9]+|\s+[0-9A-za-z])|released under|license|\u00a9|^@ui5-bundle-raw-include |^@ui5-bundle /i;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also not directly related to the resources.json, right?
It thought this fixes the dependency analysis, but when reverting the pattern only the uglifier tests are failing.

So maybe some integration tests should be added to cover this scenario?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added integration test with: a3963b9

@@ -0,0 +1 @@
sap.ui.define([],function(){return{}});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also prefer to not add so many test fixtures. We should rather use our fs abstraction to have virtual files within the tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

separated additional tests into separate PR:
#504

tobiasso85 and others added 8 commits August 20, 2020 14:34
Reduce test fixtures by using the file content
string in the tests directly.
* move i18n files to separate folder
* revert some.js
* put global export logic into newly
  introduced library.designtime.js file
ensure @ui5-bundle-raw-include comment is also preserved in Builder.js
Not supported with generateStandaloneAppBundle task.
@tobiasso85 tobiasso85 changed the title [FEATURE] Generate resources.json files for libs, components, themes [FEATURE] Add generateResourcesJson task to generate resources.json which allows to generate resources.json files for libs, components, themes Aug 24, 2020
@tobiasso85 tobiasso85 changed the title [FEATURE] Add generateResourcesJson task to generate resources.json which allows to generate resources.json files for libs, components, themes [FEATURE] Add generateResourcesJson task Aug 24, 2020
@tobiasso85 tobiasso85 requested review from matz3 and tobiasso85 August 24, 2020 13:00
Copy link
Contributor

@tobiasso85 tobiasso85 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@tobiasso85 tobiasso85 merged commit 021f439 into master Aug 24, 2020
@tobiasso85 tobiasso85 deleted the create-resources-json branch August 24, 2020 13:17
RandomByte added a commit to SAP/ui5-tooling that referenced this pull request Dec 2, 2020
PRs where this process was already used:
* SAP/ui5-builder#488
* SAP/ui5-builder#390

JIRA: CPOUI5FOUNDATION-261
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants