Convert app-context-init to typescript and fix server.go

Typescript doesn't add .js extension to the imported file name. As a
result, the browser imports the same file twice with different names:
for example app-context and app-context.js. After the fix, server.go
patches the typescript output and adds .js extension. Rollup handles
such cases correctly.

Change-Id: I3290f9244bb6d6dd8547fbceed8ed57186c6f638
This commit is contained in:
Dmitrii Filippov
2020-07-29 13:16:12 +02:00
parent f1014d1fcf
commit c02c096ad2
2 changed files with 48 additions and 31 deletions

View File

@@ -14,17 +14,24 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import {appContext} from './app-context.js'; import {appContext, AppContext} from './app-context';
import {FlagsServiceImplementation} from './flags/flags_impl.js'; import {FlagsServiceImplementation} from './flags/flags_impl';
import {GrReporting} from './gr-reporting/gr-reporting_impl.js'; import {GrReporting} from './gr-reporting/gr-reporting_impl';
import {EventEmitter} from './gr-event-interface/gr-event-interface_impl.js'; import {EventEmitter} from './gr-event-interface/gr-event-interface_impl';
import {Auth} from './gr-auth/gr-auth_impl.js'; import {Auth} from './gr-auth/gr-auth_impl';
const initializedServices = new Map(); type ServiceName = keyof AppContext;
type ServiceCreator<T> = () => T;
function getService(serviceName, serviceInit) { // eslint-disable-next-line @typescript-eslint/no-explicit-any
const initializedServices: Map<ServiceName, any> = new Map();
function getService<K extends ServiceName>(
serviceName: K,
serviceCreator: ServiceCreator<AppContext[K]>
): AppContext[K] {
if (!initializedServices.has(serviceName)) { if (!initializedServices.has(serviceName)) {
initializedServices.set(serviceName, serviceInit()); initializedServices.set(serviceName, serviceCreator());
} }
return initializedServices.get(serviceName); return initializedServices.get(serviceName);
} }
@@ -33,23 +40,30 @@ function getService(serviceName, serviceInit) {
* The AppContext lazy initializator for all services * The AppContext lazy initializator for all services
*/ */
export function initAppContext() { export function initAppContext() {
const registeredServices = {}; function populateAppContext(
function addService(serviceName, serviceCreator) { serviceCreators: {[P in ServiceName]: ServiceCreator<AppContext[P]>}
if (registeredServices[serviceName]) { ) {
throw new Error(`Service ${serviceName} already registered.`); const registeredServices = Object.keys(serviceCreators).reduce(
} (registeredServices, key) => {
registeredServices[serviceName] = { const serviceName = key as ServiceName;
configurable: true, // Tests can mock properties const serviceCreator = serviceCreators[serviceName];
get() { registeredServices[serviceName] = {
return getService(serviceName, serviceCreator); configurable: true, // Tests can mock properties
get() {
return getService(serviceName, serviceCreator);
},
};
return registeredServices;
}, },
}; {} as PropertyDescriptorMap
);
Object.defineProperties(appContext, registeredServices);
} }
addService('flagsService', () => new FlagsServiceImplementation()); populateAppContext({
addService('reportingService', flagsService: () => new FlagsServiceImplementation(),
() => new GrReporting(appContext.flagsService)); reportingService: () => new GrReporting(appContext.flagsService),
addService('eventEmitter', () => new EventEmitter()); eventEmitter: () => new EventEmitter(),
addService('authService', () => new Auth(appContext.eventEmitter)); authService: () => new Auth(appContext.eventEmitter),
Object.defineProperties(appContext, registeredServices); });
} }

View File

@@ -168,17 +168,20 @@ func handleSrcRequest(compiledSrcPath string, dirListingMux *http.ServeMux, writ
writer.Header().Set("Content-Type", "text/html") writer.Header().Set("Content-Type", "text/html")
} else if isJsFile { } else if isJsFile {
// The following code updates import statements. // The following code updates import statements.
// 1. Keep all imports started with '.' character unchanged (i.e. all relative // 1. if an in imported file has .js or .mjs extension, the code keeps
// imports like import ... from './a.js' or import ... from '../b/c/d.js' // the file extension unchanged. Otherwise, it adds .js extension
// 2. For other imports it adds '/node_modules/' prefix. Additionally, // 2. For module imports it adds '/node_modules/' prefix.
// if an in imported file has .js or .mjs extension, the code keeps
// the file extension unchanged. Otherwise, it adds .js extension.
// Examples: // Examples:
// '@polymer/polymer.js' -> '/node_modules/@polymer/polymer.js' // '@polymer/polymer.js' -> '/node_modules/@polymer/polymer.js'
// 'page/page.mjs' -> '/node_modules/page.mjs' // 'page/page.mjs' -> '/node_modules/page.mjs'
// '@polymer/iron-icon' -> '/node_modules/@polymer/iron-icon.js' // '@polymer/iron-icon' -> '/node_modules/@polymer/iron-icon.js'
moduleImportRegexp := regexp.MustCompile("(?m)^(import.*)'([^/.].*?)(\\.(m?)js)?';$") // './element/file' -> './element/file.js'
data = moduleImportRegexp.ReplaceAll(data, []byte("$1 '/node_modules/$2.${4}js';")) moduleImportRegexp := regexp.MustCompile("(?m)^(import.*)'(.*?)(\\.(m?)js)?';$")
data = moduleImportRegexp.ReplaceAll(data, []byte("$1 '$2.${4}js';"))
moduleImportRegexp = regexp.MustCompile("(?m)^(import.*)'([^/.].*)';$")
data = moduleImportRegexp.ReplaceAll(data, []byte("$1 '/node_modules/$2';"))
writer.Header().Set("Content-Type", "application/javascript") writer.Header().Set("Content-Type", "application/javascript")
} else if strings.HasSuffix(normalizedContentPath, ".css") { } else if strings.HasSuffix(normalizedContentPath, ".css") {
writer.Header().Set("Content-Type", "text/css") writer.Header().Set("Content-Type", "text/css")