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
@angular-devkit/build-optimizer@0.1001.0 after prod build, only a black screen is displayed on the device #18682
Comments
This seems like a bug but we'll need to look at a reproduction to find and fix the problem. Can you setup a minimal repro please? You can read here why this is needed. A good way to make a minimal repro is to create a new app via This might be related to your directory structure so its really important to get an accurate repro to diagnose this. |
We have the same issue with newest versions of angular and devkit. Seems that SCSS styles configured in angular.json are not being included in the bundle properly: However, src/styles.scss is included. If we move SCSS references from angular.json to be imported through styles.scss, everything works correctly. This started to happen after the update this morning to angular 10.1.0 / devkit 0.1001.0. |
It wasn't just happening on devices, it was happening in production mode. It is reproduced with ng serve --prod and attaches the log.
|
@appimpact, you issue seems different, please file an new issue with a reproduction. @eun-choi, unfortunately without a reproduction it’s hard to tell what’s happening. |
It's the same issue - our interfaces are not black, but are broken after the update as SCSS files are not being loaded. |
It may be a similar issue. I'm also using several scss files. The amount of code in the current app is large, so I will try to create a compact code that can be reproduced. |
[Added Info.] e49c926#diff-d35822fd50a0465d0a3f6ddfb943b127 ab62c7a#diff-d35822fd50a0465d0a3f6ddfb943b127 These two changes are not in the release notes. |
Those 2 commits are internal refactors which shouldn't effect the applications. I'll try to go through the refactor again, maybe i can spot something. A reproduction would definitely be helpful here or at least what's the difference in emitted code between the changes it would be hard to tell what's happening. You can run the production build without mangling, which should help diff the bundles easier.
|
@alan-agius4 I will copy the code by function unit in both versions of the scrub-file.js file and check which function causes the problem to me. |
The problem occurs in function isTslibHelper() Existing code without problems:
Code causing the problem:
|
FYI, typescript@4.0.2 and tslib@2.0.1 version. The tsconfig.json configuration is as follows:
|
I think what's is happening here is that one of your libraries was compiled using an older version of TypeScript, where it used namespace imports for tslib. Can you try to update the scrub-file.js"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.expect = exports.getScrubFileTransformerForCore = exports.getScrubFileTransformer = exports.testScrubFile = void 0;
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
const ts = require("typescript");
const ast_utils_1 = require("../helpers/ast-utils");
function testScrubFile(content) {
const markers = [
'decorators',
'__decorate',
'propDecorators',
'ctorParameters',
'ɵsetClassMetadata',
];
return markers.some((marker) => content.includes(marker));
}
exports.testScrubFile = testScrubFile;
function getScrubFileTransformer(program) {
return scrubFileTransformer(program, false);
}
exports.getScrubFileTransformer = getScrubFileTransformer;
function getScrubFileTransformerForCore(program) {
return scrubFileTransformer(program, true);
}
exports.getScrubFileTransformerForCore = getScrubFileTransformerForCore;
function scrubFileTransformer(program, isAngularCoreFile) {
if (!program) {
throw new Error('scrubFileTransformer requires a TypeScript Program.');
}
const checker = program.getTypeChecker();
return (context) => {
const transformer = (sf) => {
const ngMetadata = findAngularMetadata(sf, isAngularCoreFile);
const tslibImports = findTslibImports(sf);
const nodes = [];
ts.forEachChild(sf, checkNodeForDecorators);
function checkNodeForDecorators(node) {
if (!ts.isExpressionStatement(node)) {
return ts.forEachChild(node, checkNodeForDecorators);
}
const exprStmt = node;
// Do checks that don't need the typechecker first and bail early.
if (isIvyPrivateCallExpression(exprStmt) || isCtorParamsAssignmentExpression(exprStmt)) {
nodes.push(node);
}
else if (isDecoratorAssignmentExpression(exprStmt)) {
nodes.push(...pickDecorationNodesToRemove(exprStmt, ngMetadata, checker));
}
else if (isDecorateAssignmentExpression(exprStmt, tslibImports, checker)
|| isAngularDecoratorExpression(exprStmt, ngMetadata, tslibImports, checker)) {
nodes.push(...pickDecorateNodesToRemove(exprStmt, tslibImports, ngMetadata, checker));
}
else if (isPropDecoratorAssignmentExpression(exprStmt)) {
nodes.push(...pickPropDecorationNodesToRemove(exprStmt, ngMetadata, checker));
}
}
const visitor = (node) => {
// Check if node is a statement to be dropped.
if (nodes.find((n) => n === node)) {
return undefined;
}
// Otherwise return node as is.
return ts.visitEachChild(node, visitor, context);
};
return ts.visitNode(sf, visitor);
};
return transformer;
};
}
function expect(node, kind) {
if (node.kind !== kind) {
throw new Error('Invalid node type.');
}
return node;
}
exports.expect = expect;
function findAngularMetadata(node, isAngularCoreFile) {
let specs = [];
// Find all specifiers from imports of `@angular/core`.
ts.forEachChild(node, (child) => {
if (child.kind === ts.SyntaxKind.ImportDeclaration) {
const importDecl = child;
if (isAngularCoreImport(importDecl, isAngularCoreFile)) {
specs.push(...ast_utils_1.collectDeepNodes(importDecl, ts.SyntaxKind.ImportSpecifier));
}
}
});
// If the current module is a Angular core file, we also consider all declarations in it to
// potentially be Angular metadata.
if (isAngularCoreFile) {
const localDecl = findAllDeclarations(node);
specs = specs.concat(localDecl);
}
return specs;
}
function findAllDeclarations(node) {
const nodes = [];
ts.forEachChild(node, (child) => {
if (child.kind === ts.SyntaxKind.VariableStatement) {
const vStmt = child;
vStmt.declarationList.declarations.forEach((decl) => {
if (decl.name.kind !== ts.SyntaxKind.Identifier) {
return;
}
nodes.push(decl);
});
}
});
return nodes;
}
function isAngularCoreImport(node, isAngularCoreFile) {
if (!(node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier))) {
return false;
}
const importText = node.moduleSpecifier.text;
// Imports to `@angular/core` are always core imports.
if (importText === '@angular/core') {
return true;
}
// Relative imports from a Angular core file are also core imports.
if (isAngularCoreFile && (importText.startsWith('./') || importText.startsWith('../'))) {
return true;
}
return false;
}
// Check if assignment is `Clazz.decorators = [...];`.
function isDecoratorAssignmentExpression(exprStmt) {
if (!isAssignmentExpressionTo(exprStmt, 'decorators')) {
return false;
}
const expr = exprStmt.expression;
if (!ts.isArrayLiteralExpression(expr.right)) {
return false;
}
return true;
}
// Check if assignment is `Clazz = __decorate([...], Clazz)`.
function isDecorateAssignmentExpression(exprStmt, tslibImports, checker) {
if (!ts.isBinaryExpression(exprStmt.expression)) {
return false;
}
const expr = exprStmt.expression;
if (!ts.isIdentifier(expr.left)) {
return false;
}
const classIdent = expr.left;
let callExpr;
if (ts.isCallExpression(expr.right)) {
callExpr = expr.right;
}
else if (ts.isBinaryExpression(expr.right)) {
// `Clazz = Clazz_1 = __decorate([...], Clazz)` can be found when there are static property
// accesses.
const innerExpr = expr.right;
if (!ts.isIdentifier(innerExpr.left) || !ts.isCallExpression(innerExpr.right)) {
return false;
}
callExpr = innerExpr.right;
}
else {
return false;
}
if (!isTslibHelper(callExpr, '__decorate', tslibImports, checker)) {
return false;
}
if (callExpr.arguments.length !== 2) {
return false;
}
const classArg = callExpr.arguments[1];
if (!ts.isIdentifier(classArg)) {
return false;
}
if (classIdent.text !== classArg.text) {
return false;
}
if (!ts.isArrayLiteralExpression(callExpr.arguments[0])) {
return false;
}
return true;
}
// Check if expression is `__decorate([smt, __metadata("design:type", Object)], ...)`.
function isAngularDecoratorExpression(exprStmt, ngMetadata, tslibImports, checker) {
if (!ts.isCallExpression(exprStmt.expression)) {
return false;
}
const callExpr = exprStmt.expression;
if (!isTslibHelper(callExpr, '__decorate', tslibImports, checker)) {
return false;
}
if (callExpr.arguments.length !== 4) {
return false;
}
const decorateArray = callExpr.arguments[0];
if (!ts.isArrayLiteralExpression(decorateArray)) {
return false;
}
// Check first array entry for Angular decorators.
if (decorateArray.elements.length === 0 || !ts.isCallExpression(decorateArray.elements[0])) {
return false;
}
return decorateArray.elements.some(decoratorCall => {
if (!ts.isCallExpression(decoratorCall) || !ts.isIdentifier(decoratorCall.expression)) {
return false;
}
const decoratorId = decoratorCall.expression;
return identifierIsMetadata(decoratorId, ngMetadata, checker);
});
}
// Check if assignment is `Clazz.propDecorators = [...];`.
function isPropDecoratorAssignmentExpression(exprStmt) {
if (!isAssignmentExpressionTo(exprStmt, 'propDecorators')) {
return false;
}
const expr = exprStmt.expression;
if (expr.right.kind !== ts.SyntaxKind.ObjectLiteralExpression) {
return false;
}
return true;
}
// Check if assignment is `Clazz.ctorParameters = [...];`.
function isCtorParamsAssignmentExpression(exprStmt) {
if (!isAssignmentExpressionTo(exprStmt, 'ctorParameters')) {
return false;
}
const expr = exprStmt.expression;
if (expr.right.kind !== ts.SyntaxKind.FunctionExpression
&& expr.right.kind !== ts.SyntaxKind.ArrowFunction) {
return false;
}
return true;
}
function isAssignmentExpressionTo(exprStmt, name) {
if (!ts.isBinaryExpression(exprStmt.expression)) {
return false;
}
const expr = exprStmt.expression;
if (!ts.isPropertyAccessExpression(expr.left)) {
return false;
}
const propAccess = expr.left;
if (propAccess.name.text !== name) {
return false;
}
if (!ts.isIdentifier(propAccess.expression)) {
return false;
}
if (expr.operatorToken.kind !== ts.SyntaxKind.FirstAssignment) {
return false;
}
return true;
}
function isIvyPrivateCallExpression(exprStmt) {
// Each Ivy private call expression is inside an IIFE as single statements, so we must go down it.
const expression = exprStmt.expression;
if (!expression || !ts.isCallExpression(expression) || expression.arguments.length !== 0) {
return null;
}
const parenExpr = expression;
if (!ts.isParenthesizedExpression(parenExpr.expression)) {
return null;
}
const funExpr = parenExpr.expression.expression;
if (!ts.isFunctionExpression(funExpr)) {
return null;
}
const innerStmts = funExpr.body.statements;
if (innerStmts.length !== 1) {
return null;
}
const innerExprStmt = innerStmts[0];
if (!ts.isExpressionStatement(innerExprStmt)) {
return null;
}
// Now we're in the IIFE and have the inner expression statement. We can check if it matches
// a private Ivy call.
const callExpr = innerExprStmt.expression;
if (!ts.isCallExpression(callExpr)) {
return false;
}
const propAccExpr = callExpr.expression;
if (!ts.isPropertyAccessExpression(propAccExpr)) {
return false;
}
if (propAccExpr.name.text !== 'ɵsetClassMetadata') {
return false;
}
return true;
}
// Remove Angular decorators from`Clazz.decorators = [...];`, or expression itself if all are
// removed.
function pickDecorationNodesToRemove(exprStmt, ngMetadata, checker) {
const expr = expect(exprStmt.expression, ts.SyntaxKind.BinaryExpression);
const literal = expect(expr.right, ts.SyntaxKind.ArrayLiteralExpression);
if (!literal.elements.every(elem => ts.isObjectLiteralExpression(elem))) {
return [];
}
const elements = literal.elements;
const ngDecorators = elements.filter((elem) => isAngularDecorator(elem, ngMetadata, checker));
return (elements.length > ngDecorators.length) ? ngDecorators : [exprStmt];
}
// Remove Angular decorators from `Clazz = __decorate([...], Clazz)`, or expression itself if all
// are removed.
function pickDecorateNodesToRemove(exprStmt, tslibImports, ngMetadata, checker) {
let callExpr;
if (ts.isCallExpression(exprStmt.expression)) {
callExpr = exprStmt.expression;
}
else if (ts.isBinaryExpression(exprStmt.expression)) {
const expr = exprStmt.expression;
if (ts.isCallExpression(expr.right)) {
callExpr = expr.right;
}
else if (ts.isBinaryExpression(expr.right) && ts.isCallExpression(expr.right.right)) {
callExpr = expr.right.right;
}
}
if (!callExpr) {
return [];
}
const arrLiteral = expect(callExpr.arguments[0], ts.SyntaxKind.ArrayLiteralExpression);
if (!arrLiteral.elements.every((elem) => ts.isCallExpression(elem))) {
return [];
}
const elements = arrLiteral.elements;
const ngDecoratorCalls = elements.filter((el) => {
if (!ts.isIdentifier(el.expression)) {
return false;
}
return identifierIsMetadata(el.expression, ngMetadata, checker);
});
// Remove __metadata calls of type 'design:paramtypes'.
const metadataCalls = elements.filter((el) => {
if (!isTslibHelper(el, '__metadata', tslibImports, checker)) {
return false;
}
if (el.arguments.length < 2 || !ts.isStringLiteral(el.arguments[0])) {
return false;
}
return true;
});
// Remove all __param calls.
const paramCalls = elements.filter((el) => {
if (!isTslibHelper(el, '__param', tslibImports, checker)) {
return false;
}
if (el.arguments.length !== 2 || !ts.isNumericLiteral(el.arguments[0])) {
return false;
}
return true;
});
ngDecoratorCalls.push(...metadataCalls, ...paramCalls);
// If all decorators are metadata decorators then return the whole `Class = __decorate([...])'`
// statement so that it is removed in entirety
return (elements.length === ngDecoratorCalls.length) ? [exprStmt] : ngDecoratorCalls;
}
// Remove Angular decorators from`Clazz.propDecorators = [...];`, or expression itself if all
// are removed.
function pickPropDecorationNodesToRemove(exprStmt, ngMetadata, checker) {
const expr = expect(exprStmt.expression, ts.SyntaxKind.BinaryExpression);
const literal = expect(expr.right, ts.SyntaxKind.ObjectLiteralExpression);
if (!literal.properties.every(elem => ts.isPropertyAssignment(elem)
&& ts.isArrayLiteralExpression(elem.initializer))) {
return [];
}
const assignments = literal.properties;
// Consider each assignment individually. Either the whole assignment will be removed or
// a particular decorator within will.
const toRemove = assignments
.map((assign) => {
const decorators = expect(assign.initializer, ts.SyntaxKind.ArrayLiteralExpression).elements;
if (!decorators.every((el) => ts.isObjectLiteralExpression(el))) {
return [];
}
const decsToRemove = decorators.filter((expression) => {
const lit = expect(expression, ts.SyntaxKind.ObjectLiteralExpression);
return isAngularDecorator(lit, ngMetadata, checker);
});
if (decsToRemove.length === decorators.length) {
return [assign];
}
return decsToRemove;
})
.reduce((accum, toRm) => accum.concat(toRm), []);
// If every node to be removed is a property assignment (full property's decorators) and
// all properties are accounted for, remove the whole assignment. Otherwise, remove the
// nodes which were marked as safe.
if (toRemove.length === assignments.length && toRemove.every((node) => ts.isPropertyAssignment(node))) {
return [exprStmt];
}
return toRemove;
}
function isAngularDecorator(literal, ngMetadata, checker) {
const types = literal.properties.filter(isTypeProperty);
if (types.length !== 1) {
return false;
}
const assign = expect(types[0], ts.SyntaxKind.PropertyAssignment);
if (!ts.isIdentifier(assign.initializer)) {
return false;
}
const id = assign.initializer;
const res = identifierIsMetadata(id, ngMetadata, checker);
return res;
}
function isTypeProperty(prop) {
if (!ts.isPropertyAssignment(prop)) {
return false;
}
if (!ts.isIdentifier(prop.name)) {
return false;
}
return prop.name.text === 'type';
}
// Check if an identifier is part of the known Angular Metadata.
function identifierIsMetadata(id, metadata, checker) {
const symbol = checker.getSymbolAtLocation(id);
if (!symbol || !symbol.declarations || !symbol.declarations.length) {
return false;
}
return symbol
.declarations
.some((spec) => metadata.includes(spec));
}
// Find all named imports for `tslib`.
function findTslibImports(node) {
const imports = [];
ts.forEachChild(node, child => {
var _a;
if (ts.isImportDeclaration(child) &&
child.moduleSpecifier &&
ts.isStringLiteral(child.moduleSpecifier) &&
child.moduleSpecifier.text === 'tslib' &&
!!((_a = child.importClause) === null || _a === void 0 ? void 0 : _a.namedBindings)) {
imports.push(child.importClause.namedBindings);
}
});
return imports;
}
// Check if a function call is a tslib helper.
function isTslibHelper(callExpr, helper, tslibImports, checker) {
var _a;
let identifier;
if (ts.isPropertyAccessExpression(callExpr.expression)
&& callExpr.expression.name.text === helper
&& ts.isIdentifier(callExpr.expression.expression)) {
// for namespaced imports
identifier = callExpr.expression.expression;
}
else if (ts.isIdentifier(callExpr.expression) && callExpr.expression.text === helper) {
// for named imports
identifier = callExpr.expression;
}
else {
return false;
}
const symbol = checker.getSymbolAtLocation(identifier);
if (!((_a = symbol === null || symbol === void 0 ? void 0 : symbol.declarations) === null || _a === void 0 ? void 0 : _a.length)) {
return false;
}
for (const name of tslibImports) {
for (const dec of symbol.declarations) {
if (ts.isNamedImports(name)) {
if (ts.isImportSpecifier(dec) && name.elements.includes(dec)) {
return true;
}
}
else if (dec === name) {
// Namespace import
return true;
}
}
}
return false;
} |
Trying with the changed function still doesn't work.
Fortunately, I found libraries with problems.
I created a git page related to this. Steps to reproduce:
|
Thanks for the reproduction. That should help us track down the problem much quicker. |
By the way @eun-choi, I noticed you a Rio Ed that you tried to change the function. That alone is not enough you need to overwrite the file. Anyways, I’ll take at the repro in the next couple of days. |
Tested by overwriting the file, it was the same. |
HI, I started looking and there are a multiple reasons why using
I will try to land a fix for this soon, however I strongly suggest that you reach out the library author to address the mentioned issues. |
@alan-agius4 Okay. I'll share this issue with the developers of the 2 libraries. (kakao-sdk & @admob-plus/ionic) |
…lib helpers are inlined Closes #18682
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
🐞 Bug report
Command (mark with an
x
)Description
Install @angular-devkit/build-angular@0.1001.0 or @angular-devkit/build-angular@0.1001.0-rc.0
After build, only a black screen is displayed on the device and the app cannot be used.
FYI, it works without problem until version @angular-devkit/build-angular@0.1000.8 or @angular-devkit/build-angular@0.1001.0-next.7
🔬 Minimal Reproduction
🌍 Your Environment
The text was updated successfully, but these errors were encountered: