Skip to content

Commit cb5d3f2

Browse files
authored
Merge pull request #1069 from izaera/LPS-172747
feat: support internal path exports in npm-scripts
2 parents e3fface + eb2fce5 commit cb5d3f2

File tree

3 files changed

+150
-42
lines changed

3 files changed

+150
-42
lines changed

projects/npm-tools/packages/npm-scripts/src/utils/createEsm2AmdExportsBridges.js

Lines changed: 74 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ function createEsm2AmdExportsBridges(projectDir, buildConfig, manifest) {
3232
const {exports, output} = buildConfig;
3333

3434
exports.forEach((exportItem) => {
35-
const {pkgName, scope} = splitModuleName(exportItem.name);
35+
const splittedModuleName = splitModuleName(exportItem.name);
36+
const {pkgName, scope} = splittedModuleName;
37+
let {modulePath} = splittedModuleName;
38+
39+
// Compute src and destination package.json objects
3640

3741
const scopedPkgName = joinModuleName(scope, pkgName, '');
3842

@@ -42,15 +46,21 @@ function createEsm2AmdExportsBridges(projectDir, buildConfig, manifest) {
4246
basedir: projectDir,
4347
}
4448
));
45-
const srcPkgId = `${srcPkgJson.name}@${srcPkgJson.version}`;
4649

4750
const pkgJson = {
4851
dependencies: {},
49-
main: './index.js',
5052
name: addNamespace(scopedPkgName, rootPkgJson),
5153
version: srcPkgJson.version,
5254
};
53-
const pkgId = `${pkgJson.name}@${pkgJson.version}`;
55+
56+
// Tweak module path and package.json main's field
57+
58+
if (!modulePath) {
59+
modulePath = '/index';
60+
pkgJson.main = './index.js';
61+
}
62+
63+
// Create output package dir
5464

5565
const packageDir = path.join(
5666
output,
@@ -61,6 +71,16 @@ function createEsm2AmdExportsBridges(projectDir, buildConfig, manifest) {
6171
})
6272
);
6373

74+
fs.mkdirSync(packageDir, {recursive: true});
75+
76+
// Create/update output package.json file
77+
78+
fs.writeFileSync(
79+
path.join(packageDir, 'package.json'),
80+
JSON.stringify(pkgJson, null, '\t'),
81+
'utf8'
82+
);
83+
6484
//
6585
// Compute the relative position of the bridge related to the real ES
6686
// module.
@@ -71,18 +91,34 @@ function createEsm2AmdExportsBridges(projectDir, buildConfig, manifest) {
7191
// Also, depending for npm-scoped scoped packages, and additional folder
7292
// level appears under `/o/js/resolved-module/...`.
7393
//
94+
// Also, for internal module paths, we must add more `../`s.
95+
//
96+
97+
let rootDir = '../..';
98+
99+
if (exportItem.name.startsWith('@')) {
100+
rootDir += '/..';
101+
}
102+
103+
const hopsToAdd = modulePath.split('/').length - 1;
104+
105+
for (let i = 0; i < hopsToAdd; i++) {
106+
rootDir += '/..';
107+
}
108+
109+
rootDir += webContextPath;
74110

75-
const rootDir = exportItem.name.startsWith('@')
76-
? `../../../..${webContextPath}`
77-
: `../../..${webContextPath}`;
111+
// Create output bridge file
112+
113+
const pkgId = `${pkgJson.name}@${pkgJson.version}`;
78114

79115
const bridgeSource = `
80116
import * as esModule from "${rootDir}/__liferay__/exports/${flattenPkgName(
81117
exportItem.name
82118
)}.js";
83119
84120
Liferay.Loader.define(
85-
"${pkgId}/index",
121+
"${pkgId}${modulePath}",
86122
['module'],
87123
function (module) {
88124
module.exports = {
@@ -94,39 +130,40 @@ Liferay.Loader.define(
94130
);
95131
`;
96132

97-
fs.mkdirSync(packageDir, {recursive: true});
98-
99-
fs.writeFileSync(
100-
path.join(packageDir, 'package.json'),
101-
JSON.stringify(pkgJson, null, '\t'),
102-
'utf8'
133+
const outputFilePath = path.join(
134+
packageDir,
135+
`${modulePath.substr(1).replace('/', path.sep)}.js`
103136
);
104137

105-
fs.writeFileSync(
106-
path.join(packageDir, 'index.js'),
107-
bridgeSource,
108-
'utf8'
109-
);
138+
fs.mkdirSync(path.dirname(outputFilePath), {recursive: true});
110139

111-
manifest.packages[pkgId] = manifest.packages[pkgId] || {
112-
dest: {
113-
dir: '.',
114-
id: pkgId,
115-
name: pkgJson.name,
116-
version: pkgJson.version,
117-
},
118-
modules: {
119-
'index.js': {
120-
flags: {
121-
esModule: true,
122-
useESM: true,
123-
},
140+
fs.writeFileSync(outputFilePath, bridgeSource, 'utf8');
141+
142+
// Update output manifest.json
143+
144+
if (!manifest.packages[pkgId]) {
145+
const srcPkgId = `${srcPkgJson.name}@${srcPkgJson.version}`;
146+
147+
manifest.packages[pkgId] = {
148+
dest: {
149+
dir: '.',
150+
id: pkgId,
151+
name: pkgJson.name,
152+
version: pkgJson.version,
124153
},
125-
},
126-
src: {
127-
id: srcPkgId,
128-
name: srcPkgJson.name,
129-
version: srcPkgJson.version,
154+
modules: {},
155+
src: {
156+
id: srcPkgId,
157+
name: srcPkgJson.name,
158+
version: srcPkgJson.version,
159+
},
160+
};
161+
}
162+
163+
manifest.packages[pkgId].modules[`${modulePath.substr(1)}.js`] = {
164+
flags: {
165+
esModule: true,
166+
useESM: true,
130167
},
131168
};
132169
});

projects/npm-tools/packages/npm-scripts/src/utils/runWebpackAsBundler.js

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
/* eslint-disable @liferay/no-dynamic-require */
77

8+
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
89
const path = require('path');
910
const resolve = require('resolve');
1011
const TerserPlugin = require('terser-webpack-plugin');
@@ -40,13 +41,16 @@ module.exports = async function runWebpackAsBundler(
4041
await runWebpack(indexWebpackConfig, buildConfig.report);
4142
}
4243

43-
const webpackConfigs = getImportsWebpackConfigs(buildConfig);
44+
const exportsWebpackConfig = getExportsWebpackConfig(
45+
projectDir,
46+
buildConfig
47+
);
4448

4549
let i = 0;
4650

47-
for (const webpackConfig of webpackConfigs) {
51+
for (const webpackConfig of exportsWebpackConfig) {
4852
createTempFile(
49-
`webpackAsBundler.import[${i++}].config.json`,
53+
`webpackAsBundler.export[${i++}].config.json`,
5054
JSON.stringify(webpackConfig, null, 2),
5155
{autoDelete: false}
5256
);
@@ -261,7 +265,7 @@ ${nonDefaultFields}
261265
};
262266
}
263267

264-
function getImportsWebpackConfigs(buildConfig) {
268+
function getExportsWebpackConfig(projectDir, buildConfig) {
265269
const {exports, imports} = buildConfig;
266270

267271
const allExternals = convertImportsToExternals(imports, 3);
@@ -277,9 +281,19 @@ function getImportsWebpackConfigs(buildConfig) {
277281

278282
delete externals[pkgName];
279283

284+
// Compute output file name: for the case of .css files, we want webpack
285+
// to create a .js file with the same name as the CSS file and next to
286+
// its output. That file is never used (as webpack leaves it empty), but
287+
// it allows our exports CSS loader to put the valid .js stub in the
288+
// proper place (__liferay__/exports).
289+
290+
const entryKey = importPath.endsWith('.css')
291+
? `__liferay__/css/${flatPkgName.replace(/\.css$/, '')}`
292+
: `__liferay__/exports/${flatPkgName}`;
293+
280294
const webpackConfig = {
281295
entry: {
282-
[`__liferay__/exports/${flatPkgName}`]: {
296+
[entryKey]: {
283297
import: importPath,
284298
},
285299
},
@@ -327,13 +341,40 @@ function getImportsWebpackConfigs(buildConfig) {
327341
},
328342
path: path.resolve(buildConfig.output),
329343
},
344+
plugins: [new MiniCssExtractPlugin()],
330345
resolve: {
331346
fallback: {
332347
path: false,
333348
},
334349
},
335350
};
336351

352+
// For CSS exports, add our loader so that the .js stub is created
353+
354+
if (importPath.endsWith('.css')) {
355+
webpackConfig.module.rules.push({
356+
include: /node_modules/,
357+
test: new RegExp(`${importPath.replace('/', '\\/')}$`),
358+
use: [
359+
{
360+
loader: require.resolve('./webpackExportCssLoader'),
361+
options: {
362+
filename:
363+
entryKey.replace('/css/', '/exports/') + '.css',
364+
projectDir,
365+
url: entryKey + '.css',
366+
},
367+
},
368+
{
369+
loader: MiniCssExtractPlugin.loader,
370+
},
371+
{
372+
loader: require.resolve('css-loader'),
373+
},
374+
],
375+
});
376+
}
377+
337378
if (process.env.NODE_ENV === 'development') {
338379
webpackConfig.devtool = 'source-map';
339380
webpackConfig.mode = 'development';
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* SPDX-FileCopyrightText: © 2019 Liferay, Inc. <https://liferay.com>
3+
* SPDX-License-Identifier: BSD-3-Clause
4+
*/
5+
6+
/* eslint-disable @liferay/no-dynamic-require */
7+
8+
const getBndWebContextPath = require('./getBndWebContextPath');
9+
10+
module.exports = function (content, _map, _meta) {
11+
const {filename, projectDir, url} = this.getOptions();
12+
13+
const webContextPath = getBndWebContextPath(projectDir);
14+
15+
this.emitFile(
16+
filename + '.js',
17+
`
18+
const link = document.createElement('link');
19+
link.setAttribute('rel', 'stylesheet');
20+
link.setAttribute('type', 'text/css');
21+
link.setAttribute(
22+
'href',
23+
Liferay.ThemeDisplay.getPathContext() + '/o${webContextPath}/${url}'
24+
);
25+
document.querySelector('head').appendChild(link);
26+
`
27+
);
28+
29+
return content;
30+
};

0 commit comments

Comments
 (0)