Merge pull request #1440 from overleaf/as-amd-webpack

Bundle all frontend code with webpack

GitOrigin-RevId: 1bd93dad516c456fe1649193868e841e20459b0b
This commit is contained in:
Alasdair Smith 2019-10-16 11:10:54 +01:00 committed by sharelatex
parent e27577bd5b
commit 9cf73f965c
59 changed files with 1466 additions and 1520 deletions

View file

@ -3,4 +3,3 @@ modules/**/scripts
public/js
public/minjs
modules/**/public/js
test/unit_frontend/js

View file

@ -42,31 +42,14 @@ TpdsWorker.js
BackgroundJobsWorker.js
UserAndProjectPopulator.coffee
public/es/modules
public/js/*.js
public/js/*.map
public/js/analytics/
public/js/directives/
public/js/components/
public/js/es/
public/js/filters/
public/js/ide/
public/js/main/
public/js/modules/
public/js/services/
public/js/utils/
public/js/manifest.json
public/minjs/
public/stylesheets/style*.css
public/stylesheets/sl-style*.css
public/stylesheets/light-style*.css
public/stylesheets/ieee-style*.css
public/stylesheets/*.map
public/minjs/
public/js/libs/require*.js
test/unit_frontend/js/
Gemfile.lock

View file

@ -3,4 +3,3 @@ modules/**/scripts
public/js
public/minjs
modules/**/public/js
test/unit_frontend/js

View file

@ -1,8 +1,5 @@
{
"files.exclude": {
"app/js": true,
"public/js": true,
"test/unit_frontend/js": true,
"node_modules": true,
"data": true
}

View file

@ -1,84 +0,0 @@
/* eslint-disable
max-len,
no-unused-vars,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const fs = require('fs')
const PackageVersions = require('./app/src/infrastructure/PackageVersions')
const Settings = require('settings-sharelatex')
require('es6-promise').polyfill()
module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-requirejs')
grunt.loadNpmTasks('grunt-file-append')
const config = {
requirejs: {
compile: {
options: {
optimize: 'uglify2',
appDir: 'public/js',
baseUrl: './',
dir: 'public/minjs',
inlineText: false,
generateSourceMaps: true,
preserveLicenseComments: false,
paths: {
moment: `libs/${PackageVersions.lib('moment')}`,
mathjax: '/js/libs/mathjax/MathJax.js?config=TeX-AMS_HTML',
'pdfjs-dist/build/pdf': `libs/${PackageVersions.lib('pdfjs')}/pdf`,
ace: `${PackageVersions.lib('ace')}`,
fineuploader: `libs/${PackageVersions.lib('fineuploader')}`,
recurly: 'https://js.recurly.com/v4/recurly'
},
skipDirOptimize: true,
modules: [
{
name: 'main',
exclude: ['libraries']
},
{
name: 'ide',
exclude: ['pdfjs-dist/build/pdf', 'libraries']
},
{
name: 'libraries'
},
{
name: 'ace/mode-latex'
},
{
name: 'ace/worker-latex'
}
]
}
}
},
file_append: {
default_options: {
files: [
{
append: '\n//ide.js is complete - used for automated testing',
input: 'public/minjs/ide.js',
output: 'public/minjs/ide.js'
}
]
}
}
}
grunt.initConfig(config)
return grunt.registerTask(
'compile:minify',
'Concat and minify the client side js',
['requirejs', 'file_append']
)
}

View file

@ -1,6 +1,5 @@
DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml --log-level ERROR
BUILD_NUMBER ?= local
BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
PROJECT_NAME = web
@ -16,16 +15,9 @@ MODULE_DIRS := $(shell find modules -mindepth 1 -maxdepth 1 -type d -not -name '
MODULE_MAKEFILES := $(MODULE_DIRS:=/Makefile)
MODULE_NAME=$(shell basename $(MODULE))
BABEL := node_modules/.bin/babel
GRUNT := node_modules/.bin/grunt
LESSC := node_modules/.bin/lessc
CLEANCSS := node_modules/.bin/cleancss
SRC_FILES := $(shell find public/src -name '*.js')
DIST_FILES := $(subst src,js,$(SRC_FILES))
MAIN_SRC_FILES := $(shell find modules -type f -wholename '*main/index.js')
IDE_SRC_FILES := $(shell find modules -type f -wholename '*ide/index.js')
LESS_FILES := $(shell find public/stylesheets -name '*.less')
LESSC_COMMON_FLAGS := --source-map --autoprefix="last 2 versions, ie >= 10"
CLEANCSS_FLAGS := --s0 --source-map
@ -41,45 +33,6 @@ CSS_OL_IEEE_FILE := public/stylesheets/ieee-style.css
CSS_FILES := $(CSS_SL_FILE) $(CSS_OL_FILE) $(CSS_OL_LIGHT_FILE) $(CSS_OL_IEEE_FILE)
# The automatic variable $(@D) is the target directory name
public/js/%.js: public/src/%.js
@mkdir -p $(@D)
$(BABEL) $< --out-file $@
test/unit_frontend/js/%.js: test/unit_frontend/src/%.js
@mkdir -p $(@D)
$(BABEL) $< --out-file $@
public/js/ide.js: public/src/ide.js $(IDE_SRC_FILES)
@echo Compiling and injecting module includes into public/js/ide.js
@INCLUDES=""; \
for dir in modules/*; \
do \
MODULE=`echo $$dir | cut -d/ -f2`; \
if [ -e $$dir/public/src/ide/index.js ]; then \
INCLUDES="\"ide/$$MODULE/index\",$$INCLUDES"; \
fi \
done; \
INCLUDES=$${INCLUDES%?}; \
$(BABEL) $< | \
sed -e s=\'__IDE_CLIENTSIDE_INCLUDES__\'=$$INCLUDES= \
> $@
public/js/main.js: public/src/main.js $(MAIN_SRC_FILES)
@echo Compiling and injecting module includes into public/js/main.js
@INCLUDES=""; \
for dir in modules/*; \
do \
MODULE=`echo $$dir | cut -d/ -f2`; \
if [ -e $$dir/public/src/main/index.js ]; then \
INCLUDES="\"main/$$MODULE/index\",$$INCLUDES"; \
fi \
done; \
INCLUDES=$${INCLUDES%?}; \
$(BABEL) $< | \
sed -e s=\'__MAIN_CLIENTSIDE_INCLUDES__\'=$$INCLUDES= \
> $@
public/stylesheets/%.css: $(LESS_FILES)
$(LESSC) $(LESSC_COMMON_FLAGS) $(@D)/$*.less $(@D)/$*.css
@ -87,58 +40,15 @@ css_full: $(CSS_FILES)
css: $(CSS_OL_FILE)
minify: $(CSS_FILES) $(DIST_FILES)
$(GRUNT) compile:minify
$(MAKE) minify_css
$(MAKE) minify_es
minify_css: $(CSS_FILES)
minify: $(CSS_FILES)
$(CLEANCSS) $(CLEANCSS_FLAGS) -o $(CSS_SL_FILE) $(CSS_SL_FILE)
$(CLEANCSS) $(CLEANCSS_FLAGS) -o $(CSS_OL_FILE) $(CSS_OL_FILE)
$(CLEANCSS) $(CLEANCSS_FLAGS) -o $(CSS_OL_LIGHT_FILE) $(CSS_OL_LIGHT_FILE)
$(CLEANCSS) $(CLEANCSS_FLAGS) -o $(CSS_OL_IEEE_FILE) $(CSS_OL_IEEE_FILE)
minify_es:
npm -q run webpack:production
compile: css
compile: $(DIST_FILES) css public/js/main.js public/js/ide.js
@$(MAKE) compile_modules
compile_full:
$(BABEL) public/src --out-dir public/js
$(BABEL) test/unit_frontend/src --out-dir test/unit_frontend/js
rm -f public/js/ide.js public/js/main.js # We need to generate ide.js, main.js, manually later
$(MAKE) css_full
$(MAKE) compile_modules_full
$(MAKE) compile # ide.js, main.js, share.js, and anything missed
compile_css_full:
$(MAKE) css_full
compile_modules: $(MODULE_MAKEFILES)
@set -e; \
for dir in $(MODULE_DIRS); \
do \
if [ -e $$dir/Makefile ]; then \
(cd $$dir && $(MAKE) compile); \
fi; \
if [ ! -e $$dir/Makefile ]; then \
echo "No makefile found in $$dir"; \
fi; \
done
compile_modules_full: $(MODULE_MAKEFILES)
@set -e; \
for dir in $(MODULE_DIRS); \
do \
if [ -e $$dir/Makefile ]; then \
echo "Compiling $$dir in full"; \
(cd $$dir && $(MAKE) compile_full); \
fi; \
if [ ! -e $$dir/Makefile ]; then \
echo "No makefile found in $$dir"; \
fi; \
done
compile_full: css_full
$(MODULE_MAKEFILES): Makefile.module
@set -e; \
@ -147,18 +57,9 @@ $(MODULE_MAKEFILES): Makefile.module
cp Makefile.module $$makefile; \
done
clean: clean_frontend clean_css clean_tests
clean_frontend:
rm -rf public/js/{analytics,directives,es,filters,ide,main,modules,services,utils}
rm -f public/js/*.{js,map}
clean_css:
clean:
rm -f public/stylesheets/*.css*
clean_tests:
rm -rf test/unit_frontend/js
clean_ci:
$(DOCKER_COMPOSE) down -v -t 0
docker container list | grep 'days ago' | cut -d ' ' -f 1 - | xargs -r docker container stop

View file

@ -1,16 +1,6 @@
MODULE_NAME := $(notdir $(shell pwd))
MODULE_DIR := modules/$(MODULE_NAME)
PROJECT_NAME = web
BABEL := ../../node_modules/.bin/babel
IDE_SRC_FILES := $(shell [ -e public/src/ide ] && find public/src/ide -name '*.js')
IDE_DIST_FILES := $(subst public/src/ide,../../public/js/ide/$(MODULE_NAME),$(IDE_SRC_FILES))
IDE_TEST_SRC_FILES := $(shell [ -e test/unit_frontend/src/ide ] && find test/unit_frontend/src/ide -name '*.js')
IDE_TEST_DIST_FILES := $(subst test/unit_frontend/src/ide,../../test/unit_frontend/js/ide/$(MODULE_NAME),$(IDE_TEST_SRC_FILES))
MAIN_SRC_FILES := $(shell [ -e public/src/main ] && find public/src/main -name '*.js')
MAIN_DIST_FILES := $(subst public/src/main,../../public/js/main/$(MODULE_NAME),$(MAIN_SRC_FILES))
DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml
DOCKER_COMPOSE_MODULE_FLAGS := ${DOCKER_COMPOSE_FLAGS} -f $(MODULE_DIR)/docker-compose.yml
@ -22,26 +12,5 @@ DOCKER_COMPOSE := cd ../../ && \
MOCHA_GREP=${MOCHA_GREP} \
docker-compose ${DOCKER_COMPOSE_MODULE_FLAGS}
../../test/unit_frontend/js/ide/$(MODULE_NAME)/%.js: test/unit_frontend/src/ide/%.js
@mkdir -p $(dir $@)
$(BABEL) $< --out-file $@
../../public/js/ide/$(MODULE_NAME)/%.js: public/src/ide/%.js
@mkdir -p $(dir $@)
$(BABEL) $< --out-file $@
../../public/js/main/$(MODULE_NAME)/%.js: public/src/main/%.js
@mkdir -p $(dir $@)
$(BABEL) $< --out-file $@
compile: $(IDE_DIST_FILES) $(MAIN_DIST_FILES) $(IDE_TEST_DIST_FILES)
@echo > /dev/null
compile_full:
if [ -e public/src/ide ]; then $(BABEL) public/src/ide --out-dir ../../public/js/ide/$(MODULE_NAME); fi
if [ -e public/src/main ]; then $(BABEL) public/src/main --out-dir ../../public/js/main/$(MODULE_NAME); fi
if [ -e test/unit_frontend/src/ide ]; then $(BABEL) test/unit_frontend/src/ide --out-dir ../../test/unit_frontend/js/ide/$(MODULE_NAME); fi
@$(MAKE) compile # Anything else missed
test_acceptance:
${DOCKER_COMPOSE} run --rm test_acceptance npm -q run test:acceptance:run_dir -- ${MOCHA_ARGS} $(MODULE_DIR)/test/acceptance/src

View file

@ -18,7 +18,6 @@
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
let path
const logger = require('logger-sharelatex')
const fs = require('fs')
const crypto = require('crypto')
@ -39,12 +38,34 @@ const Features = require('./Features')
Modules = require('./Modules')
const moment = require('moment')
const lodash = require('lodash')
const chokidar = require('chokidar')
const jsPath = Settings.useMinifiedJs ? '/minjs/' : '/js/'
const ace = PackageVersions.lib('ace')
const pdfjs = PackageVersions.lib('pdfjs')
const fineuploader = PackageVersions.lib('fineuploader')
const webpackManifestPath = Path.join(
__dirname,
`../../../public${jsPath}manifest.json`
)
let webpackManifest
if (['development', 'test'].includes(process.env.NODE_ENV)) {
// In dev the web and webpack containers can race (and therefore the manifest
// file may not be created when web is running), so watch the file for changes
// and reload
webpackManifest = {}
const reloadManifest = () => {
logger.log('[DEV] Reloading webpack manifest')
webpackManifest = require(webpackManifestPath)
}
logger.log('[DEV] Watching webpack manifest')
chokidar
.watch(webpackManifestPath)
.on('add', reloadManifest)
.on('change', reloadManifest)
} else {
logger.log('[PRODUCTION] Loading webpack manifest')
webpackManifest = require(webpackManifestPath)
}
const getFileContent = function(filePath) {
filePath = Path.join(__dirname, '../../../', `public${filePath}`)
@ -59,21 +80,17 @@ const getFileContent = function(filePath) {
}
const pathList = [
`${jsPath}libs/require.js`,
`${jsPath}ide.js`,
`${jsPath}main.js`,
`${jsPath}libraries.js`,
'/stylesheets/style.css',
'/stylesheets/light-style.css',
'/stylesheets/ieee-style.css',
'/stylesheets/sl-style.css'
].concat(Modules.moduleAssetFiles(jsPath))
]
if (!Settings.useMinifiedJs) {
logger.log('not using minified JS, not hashing static files')
} else {
logger.log('Generating file hashes...')
for (path of Array.from(pathList)) {
for (let path of Array.from(pathList)) {
const content = getFileContent(path)
const hash = crypto
.createHash('md5')
@ -171,56 +188,34 @@ module.exports = function(app, webRouter, privateApiRouter, publicApiRouter) {
staticFilesBase = ''
}
res.locals.jsPath = jsPath
res.locals.fullJsPath = Url.resolve(staticFilesBase, jsPath)
res.locals.lib = PackageVersions.lib
res.locals.moment = moment
res.locals.buildJsPath = function(jsFile, opts) {
if (opts == null) {
opts = {}
}
path = Path.join(jsPath, jsFile)
if (opts.hashedPath && hashedFiles[path] != null) {
path = hashedFiles[path]
}
if (opts.qs == null) {
opts.qs = {}
res.locals.buildJsPath = function(jsFile, opts = {}) {
// Resolve path from webpack manifest file
let path = webpackManifest[jsFile]
// If not found in manifest, it is directly linked, so fallback to
// relevant public directory
if (!path) {
path = Path.join(jsPath, jsFile)
}
if (opts.cdn !== false) {
path = Url.resolve(staticFilesBase, path)
}
const qs = querystring.stringify(opts.qs)
if (opts.removeExtension === true) {
path = path.slice(0, -3)
if (opts.qs) {
path = path + '?' + querystring.stringify(opts.qs)
}
if (qs != null && qs.length > 0) {
path = path + '?' + qs
}
return path
}
res.locals.buildWebpackPath = function(jsFile, opts) {
if (opts == null) {
opts = {}
}
if (Settings.webpack != null && !Settings.useMinifiedJs) {
path = Path.join(jsPath, jsFile)
if (opts.removeExtension === true) {
path = path.slice(0, -3)
}
return `${Settings.webpack.url}/public${path}`
} else {
return res.locals.buildJsPath(jsFile, opts)
}
}
res.locals.mathJaxPath = res.locals.buildJsPath('libs/mathjax/MathJax.js', {
cdn: false,
qs: { config: 'TeX-AMS_HTML,Safe' }
})
const IEEE_BRAND_ID = 15
res.locals.isIEEE = brandVariation =>
@ -249,7 +244,7 @@ module.exports = function(app, webRouter, privateApiRouter, publicApiRouter) {
res.locals.buildCssPath = function(themeModifier, buildOpts) {
const cssFileName = _buildCssFileName(themeModifier)
path = Path.join('/stylesheets/', cssFileName)
const path = Path.join('/stylesheets/', cssFileName)
if (
(buildOpts != null ? buildOpts.hashedPath : undefined) &&
hashedFiles[path] != null
@ -261,15 +256,10 @@ module.exports = function(app, webRouter, privateApiRouter, publicApiRouter) {
}
res.locals.buildImgPath = function(imgFile) {
path = Path.join('/img/', imgFile)
const path = Path.join('/img/', imgFile)
return Url.resolve(staticFilesBase, path)
}
res.locals.mathJaxPath = res.locals.buildJsPath('libs/mathjax/MathJax.js', {
cdn: false,
qs: { config: 'TeX-AMS_HTML,Safe' }
})
return next()
})
@ -421,10 +411,6 @@ module.exports = function(app, webRouter, privateApiRouter, publicApiRouter) {
}
res.locals.gaToken = Settings.analytics && Settings.analytics.ga.token
res.locals.tenderUrl = Settings.tenderUrl
res.locals.sentrySrc =
Settings.sentry != null ? Settings.sentry.src : undefined
res.locals.sentryPublicDSN =
Settings.sentry != null ? Settings.sentry.publicDSN : undefined
return next()
})

View file

@ -114,16 +114,6 @@ module.exports = Modules = {
return (Modules.viewIncludes[view] || []).length > 0
},
moduleAssetFiles(pathPrefix) {
const assetFiles = []
for (let module of Array.from(this.modules)) {
for (let assetFile of Array.from(module.assetFiles || [])) {
assetFiles.push(`${pathPrefix}${assetFile}`)
}
}
return assetFiles
},
linkedFileAgentsIncludes() {
const agents = {}
for (let module of Array.from(this.modules)) {

View file

@ -9,7 +9,10 @@ const version = {
pdfjs: '2.0.943',
moment: '2.9.0',
ace: '1.4.5', // Upgrade instructions: https://github.com/overleaf/write_latex/wiki/Upgrading-Ace
fineuploader: '5.15.4'
fineuploader: '5.15.4',
// For frontend. Backend has a different version defined in package.json
underscore: '1.9.1',
algolia: '2.5.2'
}
module.exports = {

View file

@ -53,7 +53,7 @@ html(
window.location.search += '&'+noCdnKey;
}
block scripts
block head-scripts
script(src=buildJsPath("libs/angular-1.6.4.min.js"))
@ -61,7 +61,6 @@ html(
window.sharelatex = {
siteUrl: '#{settings.siteUrl}',
wsUrl: '#{settings.wsUrl}',
jsPath: '#{jsPath}',
};
window.systemMessages = !{StringHelper.stringifyJsonForScript(systemMessages)};
window.ab = {};
@ -116,33 +115,9 @@ html(
span(ng-controller="ScribtexPopupController")
include scribtex-modal
block requirejs
block requirejs-config
script(type='text/javascript').
// minimal requirejs configuration (can be extended/overridden)
window.requirejs = {
"paths" : {
"moment": "libs/#{lib('moment')}",
"fineuploader": "libs/#{lib('fineuploader')}",
"main": "#{buildJsPath('main.js', {hashedPath:settings.useMinifiedJs, removeExtension:true})}",
"libraries": "#{buildJsPath('libraries.js', {hashedPath:settings.useMinifiedJs, removeExtension:true})}"
},
"config":{
"moment":{
"noGlobal": true
}
}
};
block requirejs-init
script(
data-main=buildJsPath('main.js', {hashedPath:false}),
baseurl=fullJsPath,
src=buildJsPath('libs/require.js', {hashedPath:true})
)
!= moduleIncludes("contactModal", locals)
include v1-tooltip
include sentry
block foot-scripts
script(src=buildJsPath('libraries.js'))
script(src=buildJsPath('main.js'))

View file

@ -113,16 +113,6 @@ block content
h3 {{ title }}
.modal-body(ng-bind-html="message")
script(src=mathJaxPath)
block requirejs
script(type="text/javascript" src='/socket.io/socket.io.js')
//- don't use cdn for workers
- var aceWorkerPath = buildJsPath(lib('ace'), {cdn:false})
- var pdfWorkerPath = buildJsPath('/libs/' + lib('pdfjs') + '/pdf.worker', {cdn:false})
- var pdfCMapsPath = buildJsPath('/libs/' + lib('pdfjs') + '/bcmaps/', {cdn:false})
//- We need to do .replace(/\//g, '\\/') do that '</script>' -> '<\/script>'
//- and doesn't prematurely end the script tag.
script#data(type="application/json").
@ -146,44 +136,10 @@ block requirejs
window.trackChangesState = data.trackChangesState;
window.wikiEnabled = #{!!(settings.apis.wiki && settings.apis.wiki.url)};
window.gitBridgePublicBaseUrl = '#{gitBridgePublicBaseUrl}'
window.requirejs = {
"paths" : {
"moment": "libs/#{lib('moment')}",
"pdfjs-dist/build/pdf": "libs/#{lib('pdfjs')}/pdf",
"pdfjs-dist/build/pdf.worker": "#{pdfWorkerPath}",
"ace": "#{lib('ace')}",
"fineuploader": "libs/#{lib('fineuploader')}",
"ide": "#{buildJsPath('ide.js', {hashedPath:settings.useMinifiedJs, removeExtension:true})}",
"libraries": "#{buildJsPath('libraries.js', {hashedPath:settings.useMinifiedJs, removeExtension:true})}",
!{moduleIncludes("editor:script", locals)}
!{moduleIncludes("publish:script", locals)}
},
"waitSeconds": 0,
"shim": {
"ace/ext-searchbox": {
"deps": ["ace/ace"]
},
"ace/ext-modelist": {
"deps": ["ace/ace"]
},
"ace/ext-language_tools": {
"deps": ["ace/ace"]
},
"ace/keybinding-vim": {
"deps": ["ace/ace"]
},
"libs/jquery.ui.touch-punch": {
"deps": [ "libs/#{lib('jquery-layout')}" ]
}
},
"config":{
"moment":{
"noGlobal": true
}
}
};
window.aceWorkerPath = "#{aceWorkerPath}";
window.pdfCMapsPath = "#{pdfCMapsPath}"
//- Set base path for Ace scripts loaded on demand/workers and don't use cdn
window.aceBasePath = "#{buildJsPath(lib('ace'), {cdn:false})}"
//- Set path for PDFjs CMaps
window.pdfCMapsPath = "#{buildJsPath('cmaps/')}"
window.uiConfig = JSON.parse('!{StringHelper.stringifyJsonForScript(uiConfig)}');
//- enable doc hash checking for all projects
//- used in public/js/libs/sharejs.js
@ -193,9 +149,8 @@ block requirejs
script(type='text/javascript').
window.overallThemes = JSON.parse('!{StringHelper.stringifyJsonForScript(overallThemes)}');
script(
data-main=buildJsPath("ide.js", {hashedPath:false}),
baseurl=fullJsPath,
data-ace-base=buildJsPath(lib('ace')),
src=buildJsPath('libs/require.js', {hashedPath:true})
)
block foot-scripts
script(type="text/javascript" src='/socket.io/socket.io.js')
script(src=mathJaxPath)
script(src=buildJsPath('libraries.js'))
script(src=buildJsPath('ide.js'))

View file

@ -1,6 +1,6 @@
extends ../layout
block scripts
block head-scripts
script(type='text/javascript').
$(document).ready(function () {
$.ajax({dataType: "script", cache: true, url: "//connect.facebook.net/en_US/all.js"}).done(function () {

View file

@ -1,88 +0,0 @@
- if (typeof(sentryPublicDSN) != "undefined")
script(type="text/javascript").
require.config({
paths: {
'raven': 'libs/raven-3.27.0.min'
}
});
require(["raven"], function(Raven) {
if (typeof(Raven) != "undefined" && Raven.config) {
Raven.config("#{sentryPublicDSN}", {
tags: { 'commit': '@@COMMIT@@', 'build': '@@RELEASE@@' },
release: '@@RELEASE@@',
// Ignore list based off: https://gist.github.com/1878283
ignoreErrors: [
'DealPly',
// Random plugins/extensions
'top.GLOBALS',
// See: http://blog.errorception.com/2012/03/tale-of-unfindable-js-error.html
'originalCreateNotification',
'canvas.contentDocument',
'MyApp_RemoveAllHighlights',
'http://tt.epicplay.com',
'Can\'t find variable: ZiteReader',
'jigsaw is not defined',
'ComboSearch is not defined',
'http://loading.retry.widdit.com/',
'atomicFindClose',
// Facebook borked
'fb_xd_fragment',
// ISP optimizing proxy - `Cache-Control: no-transform` seems to reduce this. (thanks @acdha)
// See http://stackoverflow.com/questions/4113268/how-to-stop-javascript-injection-from-vodafone-proxy
'bmi_SafeAddOnload',
'EBCallBackMessageReceived',
// See http://toolbar.conduit.com/Developer/HtmlAndGadget/Methods/JSInjection.aspx
'conduitPage',
'/NS_ERROR_NOT_CONNECTED/i',
"/Cannot read property 'row' of undefined/i",
'TypeError: start is undefined'
],
ignoreUrls: [
// Facebook flakiness
/graph\.facebook\.com/i,
// Facebook blocked
/connect\.facebook\.net\/en_US\/all\.js/i,
// Woopra flakiness
/eatdifferent\.com\.woopra-ns\.com/i,
/static\.woopra\.com\/js\/woopra\.js/i,
// Chrome extensions
/extensions\//i,
/^chrome:\/\//i,
// Other plugins
/127\.0\.0\.1:4001\/isrunning/i, // Cacaoweb
/webappstoolbarba\.texthelp\.com\//i,
/metrics\.itunes\.apple\.com\.edgesuite\.net\//i,
/a\.disquscdn\.com/i,
/platform\.twitter\.com/i,
/pstatic\.datafastguru\.info/i
],
shouldSendCallback: function(data) {
// only send a fraction of errors
var sampleRate = 0.01;
return (Math.random() <= sampleRate);
},
dataCallback: function(data) {
// remove circular references from object
var cache = [];
var s = JSON.stringify(data, function(k, v) { if (typeof v === 'object' && v !== null) { if (cache.indexOf(v) !== -1) return "[circular]"; cache.push(v); }; return v; });
return JSON.parse(s);
}
// we highly recommend restricting exceptions to a domain in order to filter out clutter
// whitelistUrls: ['example.com/scripts/']
}).install();
//- We conditionally depend on Raven (based on whether the sentry
//- config is set or not). This does not play well with require.js when
//- minifying as it is expecting all dependencies to be defined at
//- compile time. Previously Raven added itself as a global, so we just
//- mimic that old behaviour
window.Raven = Raven
}
}, function(err) {
console.log(">> error loading raven", err);
})
- if (user && typeof(user) != "undefined" && typeof (user.email) != "undefined")
script(type="text/javascript").
if (typeof(Raven) != "undefined" && Raven.setUserContext) {
Raven.setUserContext({email: '#{user.email}'});
}

View file

@ -2,7 +2,7 @@ extends ../layout
include ./dashboard/_team_name_mixin
block scripts
block head-scripts
script(src="https://js.recurly.com/v4/recurly.js")
block content

View file

@ -1,6 +1,6 @@
extends ../layout
block scripts
block head-scripts
script(src="https://js.recurly.com/v4/recurly.js")
script(type='text/javascript').
window.countryCode = '#{countryCode}'

View file

@ -6,7 +6,7 @@ include _plans_page_tables
block vars
- metadata = { viewport: true }
block scripts
block head-scripts
script(type='text/javascript').
window.recomendedCurrency = '#{recomendedCurrency}';
window.abCurrencyFlag = '#{abCurrencyFlag}';

View file

@ -1,6 +1,6 @@
extends ../../layout
block scripts
block head-scripts
script(type='text/javascript').
window.teamId = '#{teamId}'
window.hasIndividualRecurlySubscription = #{hasIndividualRecurlySubscription}

View file

@ -1,7 +1,7 @@
extends ../layout
block scripts
block head-scripts
script(type='text/javascript').
window.otherSessions = !{StringHelper.stringifyJsonForScript(sessions)}

View file

@ -1,4 +1,4 @@
block scripts
block head-scripts
script(type='text/javascript').
window.oauthProviders = !{StringHelper.stringifyJsonForScript(oauthProviders)}
window.thirdPartyIds = !{StringHelper.stringifyJsonForScript(thirdPartyIds)}

View file

View file

@ -506,7 +506,7 @@ module.exports = settings =
redirects:
"/templates/index": "/templates/"
reloadModuleViewsOnEachRequest: process.env['NODE_ENV'] != 'production'
reloadModuleViewsOnEachRequest: ['development', 'test'].includes(process.env['NODE_ENV'])
domainLicences: [

View file

@ -23,7 +23,7 @@ services:
ENABLED_LINKED_FILE_TYPES: 'url,project_file,project_output_file,mendeley,zotero'
LINKED_URL_PROXY: 'http://localhost:6543'
SHARELATEX_CONFIG: /app/test/acceptance/config/settings.test.coffee
NODE_ENV: production
NODE_ENV: test
user: root
depends_on:
- redis

View file

@ -29,7 +29,7 @@ services:
ENABLED_LINKED_FILE_TYPES: 'url,project_file,project_output_file,mendeley,zotero'
SHARELATEX_CONFIG: /app/test/acceptance/config/settings.test.coffee
MOCHA_GREP: ${MOCHA_GREP}
NODE_ENV: production
NODE_ENV: test
depends_on:
- redis
- mongo
@ -42,7 +42,7 @@ services:
volumes:
- .:/app
working_dir: /app
command: npm run test:frontend -- --single-run
command: npm run test:frontend:single
redis:
image: redis

View file

@ -3,14 +3,19 @@
make --no-print-directory format & FORMAT=$!
make --no-print-directory lint & LINT=$!
npm install git+https://github.com/sharelatex/translations-sharelatex.git#master & TRANSLATIONS=$!
WEBPACK_ENV=production make minify & MINIFY=$!
# CSS
make minify & MINIFY=$!
# JS
npm run webpack:production & WEBPACK=$!
echo "Waiting for lint, format, translations and minify to finish"
wait $LINT && echo "Lint complete" || exit 1
wait $FORMAT && echo "Format complete" || exit 1
wait $TRANSLATIONS && echo "Translations install complete" || exit 1
wait $MINIFY && echo "Minifiy complete" || exit 1
wait $MINIFY && echo "Minify complete" || exit 1
wait $WEBPACK && echo "Webpack complete" || exit 1
chmod -R 0755 /app/public
chown -R node:node /app/public

View file

@ -1,4 +1,4 @@
const path = require('path')
const webpackConfig = require('./webpack.config.test')
module.exports = function(config) {
config.set({
@ -13,64 +13,29 @@ module.exports = function(config) {
},
browsers: ['ChromeCustom'],
files: [
'test/unit_frontend/es/es-bootstrap.js',
// Angular must be loaded before requirejs to set up angular global
'test/unit_frontend/bootstrap.js',
// Include scripts that are injected into the page outside of webpack
'public/js/libs/angular-1.6.4.min.js',
'public/js/libs/angular-mocks.js',
'public/js/libs/jquery-1.11.1.min.js',
'public/js/libs/underscore-1.3.3.js',
// Set up requirejs
'test/unit_frontend/js/test-main.js',
// Include source & test files, but don't "include" them as requirejs
// handles this for us
{ pattern: 'public/js/**/*.js', included: false },
{ pattern: 'test/unit_frontend/js/**/*.js', included: false },
// Include ES test files
'test/unit_frontend/es/**/*.js',
'modules/**/test/unit_frontend/es/**/*.js',
// Allow mocking of angular
'public/js/libs/angular-mocks.js',
// Import all tests (see comment in the file for why this is necessary)
'test/unit_frontend/import_tests.js',
// Include CSS (there is some in js/libs dir)
'public/stylesheets/**/*.css',
'public/js/libs/**/*.css'
],
middleware: ['fake-img'],
preprocessors: {
// Run ES test files through webpack (which will then include source
// files in bundle)
'test/unit_frontend/es/**/*.js': ['webpack'],
'modules/**/test/unit_frontend/es/**/*.js': ['webpack']
// Run files through webpack
'test/unit_frontend/import_tests.js': ['webpack']
},
frameworks: ['requirejs', 'mocha', 'chai-sinon'],
frameworks: ['mocha', 'chai-sinon'],
// Configure webpack in the tests
webpack: {
module: {
rules: [
{
// Pass application JS files through babel-loader, compiling to ES5
test: /\.js$/,
// Only compile application files (dependencies are in ES5 already)
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
// Configure babel-loader to cache compiled output so that
// subsequent compile runs are much faster
cacheDirectory: true
}
}
]
}
]
},
resolve: {
// Alias common directories in import pathnames to cut down on the
// amount of ../../ etc
alias: {
Src: path.join(__dirname, 'public/es/'),
Modules: path.join(__dirname, 'modules')
}
}
},
webpack: webpackConfig,
// Configure the webpack dev server used to serve test files
webpackMiddleware: {
// Disable noisy CLI output
@ -78,7 +43,6 @@ module.exports = function(config) {
},
plugins: [
require('karma-chrome-launcher'),
require('karma-requirejs'),
require('karma-mocha'),
require('karma-chai-sinon'),
require('karma-webpack'),

View file

@ -1,3 +1,3 @@
// TODO: This file was created by bulk-decaffeinate.
// Sanity-check the conversion and remove this comment.
define(['main/launchpad/controllers/LaunchpadController'], function() {})
define(['./controllers/LaunchpadController'], function() {})

View file

@ -1,9 +1,7 @@
{
"exec": "make compile || exit 1",
"watch": [
"public/src/",
"public/stylesheets/",
"modules/**/public/src/"
"public/stylesheets/"
],
"ext": "js less"
"ext": "less"
}

File diff suppressed because it is too large Load diff

View file

@ -17,6 +17,7 @@
"test:unit:ci": "bin/unit_test --timeout 10000",
"test:unit:app": "bin/unit_test_app $@",
"test:frontend": "karma start",
"test:frontend:single": "karma start --single-run",
"compile": "make compile",
"start": "npm -q run compile && node $NODE_APP_OPTIONS app.js",
"nodemon": "nodemon --config nodemon.json",
@ -48,6 +49,7 @@
"contentful": "^6.1.1",
"cookie": "^0.2.3",
"cookie-parser": "1.3.5",
"crypto-js": "^3.1.9-1",
"csurf": "^1.8.3",
"d3": "^3.5.16",
"dateformat": "1.0.4-1.2.3",
@ -98,6 +100,7 @@
"passport-orcid": "0.0.3",
"passport-saml": "^1.1.0",
"passport-twitter": "^1.0.4",
"pdfjs-dist": "^2.0.943",
"pug": "^2.0.0-beta6",
"react": "^15.4.2",
"react-dom": "^15.4.2",
@ -129,7 +132,10 @@
"chai-as-promised": "^7.1.1",
"chaid": "^1.0.2",
"cheerio": "^1.0.0-rc.3",
"chokidar": "^3.1.1",
"clean-css-cli": "^4.2.1",
"copy-webpack-plugin": "^5.0.4",
"dom-testing-library": "^3.19.4",
"es6-promise": "^4.0.5",
"eslint": "^4.18.1",
"eslint-config-prettier": "^3.1.0",
@ -145,11 +151,8 @@
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-react": "^7.11.1",
"eslint-plugin-standard": "^3.0.1",
"expose-loader": "^0.7.5",
"glob": "^7.1.3",
"grunt": "0.4.5",
"grunt-cli": "^1.2.0",
"grunt-contrib-requirejs": "0.4.1",
"grunt-file-append": "0.0.6",
"handlebars-loader": "^1.7.0",
"karma": "^2.0.0",
"karma-chai-sinon": "^0.1.5",
@ -172,9 +175,11 @@
"sinon-mongoose": "^2.3.0",
"timekeeper": "^2.2.0",
"translations-sharelatex": "git+https://github.com/sharelatex/translations-sharelatex.git#master",
"val-loader": "^1.1.1",
"webpack": "^4.39.0",
"webpack-cli": "^3.3.6",
"webpack-dev-server": "^3.7.2",
"webpack-manifest-plugin": "^2.0.4",
"webpack-merge": "^4.2.1"
}
}

File diff suppressed because one or more lines are too long

View file

@ -329,4 +329,4 @@ b.length<=e}}}}},$c=function(){return{restrict:"A",require:"?ngModel",link:funct
"PM"],DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),ERANAMES:["Before Christ","Anno Domini"],ERAS:["BC","AD"],FIRSTDAYOFWEEK:6,MONTH:"January February March April May June July August September October November December".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),STANDALONEMONTH:"January February March April May June July August September October November December".split(" "),WEEKENDRANGE:[5,
6],fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",medium:"MMM d, y h:mm:ss a",mediumDate:"MMM d, y",mediumTime:"h:mm:ss a","short":"M/d/yy h:mm a",shortDate:"M/d/yy",shortTime:"h:mm a"},NUMBER_FORMATS:{CURRENCY_SYM:"$",DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{gSize:3,lgSize:3,maxFrac:3,minFrac:0,minInt:1,negPre:"-",negSuf:"",posPre:"",posSuf:""},{gSize:3,lgSize:3,maxFrac:2,minFrac:2,minInt:1,negPre:"-\u00a4",negSuf:"",posPre:"\u00a4",posSuf:""}]},id:"en-us",localeID:"en_US",pluralCat:function(a,
c){var e=a|0,f=c;void 0===f&&(f=Math.min(b(a),3));Math.pow(10,f);return 1==e&&0==f?"one":"other"}})}]),B(function(){ue(x.document,Sc)}))})(window);!window.angular.$$csp().noInlineStyle&&window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
//# sourceMappingURL=angular.min.js.map
//# sourceMappingURL=angular-1.6.4.min.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -26,7 +26,7 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(['ace/ace','libs/sha1'], function () {
define(['ace/ace','crypto-js/sha1'], function (_ignore, CryptoJSSHA1) {
var append = void 0,
bootstrapTransform = void 0,
exports = void 0,
@ -226,9 +226,6 @@ define(['ace/ace','libs/sha1'], function () {
};
};
if (typeof WEB === 'undefined') {
exports.bootstrapTransform = bootstrapTransform;
}
// A simple text implementation
//
// Operations are lists of components.
@ -673,21 +670,10 @@ define(['ace/ace','libs/sha1'], function () {
// [] is used to prevent closure from renaming types.text
exports.types.text = text;
} else {
module.exports = text;
// The text type really shouldn't need this - it should be possible to define
// an efficient transform function by making a sort of transform map and passing each
// op component through it.
require('./helpers').bootstrapTransform(text, transformComponent, checkValidOp, append);
}
// Text document API for text
if (typeof WEB === 'undefined') {
text = require('./text');
}
text.api = {
provides: { text: true },
@ -878,10 +864,6 @@ define(['ace/ace','libs/sha1'], function () {
module.exports = MicroEvent;
}
if (WEB == null) {
types = require('../types');
}
if (WEB != null) {
exports.extendDoc = function (name, fn) {
return Doc.prototype[name] = fn;
@ -1311,7 +1293,7 @@ define(['ace/ace','libs/sha1'], function () {
var needToRecomputeHash = !this.__lastSubmitTimestamp || (age > RECOMPUTE_HASH_INTERVAL) || (age < 0)
if (needToRecomputeHash || window.sl_debugging) {
// send git hash of current snapshot
var sha1 = CryptoJS.SHA1("blob " + this.snapshot.length + "\x00" + this.snapshot).toString()
var sha1 = CryptoJSSHA1("blob " + this.snapshot.length + "\x00" + this.snapshot).toString()
this.__lastSubmitTimestamp = now;
}
}
@ -1435,10 +1417,6 @@ define(['ace/ace','libs/sha1'], function () {
// Make documents event emitters
if (WEB == null) {
MicroEvent = require('./microevent');
}
MicroEvent.mixin(Doc);
exports.Doc = Doc;

View file

@ -13,7 +13,7 @@
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(['base', 'libs/md5'], function(App) {
define(['base', 'crypto-js/md5'], function(App, CryptoJS) {
const oldKeys = [
'sl_abt_multi_currency_editor_eu-eu',
'sl_abt_multi_currency_eu-eu',
@ -73,7 +73,7 @@ define(['base', 'libs/md5'], function(App) {
user_uuid = Math.random()
ipCookie(sl_user_test_token, user_uuid, { expires: 365, path: '/' })
}
const hash = CryptoJS.MD5(`${user_uuid}:${testName}`)
const hash = CryptoJS(`${user_uuid}:${testName}`)
return hash
}

View file

@ -43,7 +43,6 @@ define([
'ide/directives/layout',
'ide/directives/validFile',
'ide/services/ide',
'__IDE_CLIENTSIDE_INCLUDES__',
'analytics/AbTestingManager',
'directives/focus',
'directives/fineUpload',
@ -60,7 +59,8 @@ define([
'filters/formatDate',
'main/event',
'main/account-upgrade',
'main/exposed-settings'
'main/exposed-settings',
'../../modules/modules-ide.js'
], function(
App,
FileTreeManager,

View file

@ -14,7 +14,7 @@
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(['base', 'libs/md5'], App =>
define(['base', 'crypto-js/md5'], (App, CryptoJS) =>
App.factory('chatMessages', function($http, ide) {
const MESSAGES_URL = `/project/${ide.project_id}/messages`
const MESSAGE_LIMIT = 50
@ -152,7 +152,7 @@ define(['base', 'libs/md5'], App =>
}
var formatUser = function(user) {
const hash = CryptoJS.MD5(user.email.toLowerCase())
const hash = CryptoJS(user.email.toLowerCase())
user.gravatar_url = `//www.gravatar.com/avatar/${hash}`
return user
}

View file

@ -14,7 +14,7 @@
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define([], function() {
define(['crypto-js/md5'], function(CryptoJS) {
let ColorManager
return (ColorManager = {
getColorScheme(hue, element) {
@ -72,7 +72,7 @@ define([], function() {
},
getHueForId(id) {
const hash = CryptoJS.MD5(id)
const hash = CryptoJS(id)
let hue = parseInt(hash.toString().slice(0, 8), 16) % 320
return hue
}

View file

@ -45,10 +45,10 @@ define([
const { Vim } = ace.require('ace/keyboard/vim')
const SearchBox = ace.require('ace/ext/searchbox')
// set the path for ace workers if using a CDN (from editor.pug)
if (window.aceWorkerPath !== '') {
// Set the base path that ace will fetch modes/snippets/workers from
if (window.aceBasePath !== '') {
syntaxValidationEnabled = true
ace.config.set('workerPath', `${window.aceWorkerPath}`)
ace.config.set('basePath', `${window.aceBasePath}`)
} else {
syntaxValidationEnabled = false
}

View file

@ -17,7 +17,7 @@
*/
define([
'ide/colors/ColorManager',
'libs/md5',
'crypto-js/md5',
'ide/online-users/controllers/OnlineUsersController'
], function(ColorManager) {
let OnlineUsersManager

View file

@ -1,4 +1,4 @@
define(['base', 'pdfjs-dist/build/pdf'], (App, PDFJS) => {
define(['base', 'pdfjs-dist/webpack'], (App, PDFJS) => {
const EXTERNAL_LINK_TARGET = '_blank'
const REL_NOOPENER = 'noreferrer noopener'

View file

@ -13,7 +13,7 @@
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(['base', 'pdfjs-dist/build/pdf'], (App, PDFJS) =>
define(['base', 'pdfjs-dist/webpack'], (App, PDFJS) =>
// app = angular.module 'pdfHighlights', []
App.factory('pdfHighlights', function() {

View file

@ -18,7 +18,7 @@
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(['base', 'pdfjs-dist/build/pdf'], (App, PDFJS) =>
define(['base', 'pdfjs-dist/webpack'], (App, PDFJS) =>
// App = angular.module 'PDFRenderer', ['pdfAnnotations', 'pdfTextLayer']
App.factory('PDFRenderer', function(

View file

@ -50,64 +50,70 @@ define([
this.load = function() {
// $scope.pages = []
if ($scope.document != null) {
$scope.document.destroy()
}
$scope.loadCount = $scope.loadCount != null ? $scope.loadCount + 1 : 1
// TODO need a proper url manipulation library to add to query string
let url = $scope.pdfSrc
// add 'pdfng=true' to show that we are using the angular pdfjs viewer
const queryStringExists = /\?/.test(url)
url = url + (!queryStringExists ? '?' : '&') + 'pdfng=true'
// for isolated compiles, load the pdf on-demand because nobody will overwrite it
const onDemandLoading = true
$scope.document = new PDFRenderer(url, {
scale: 1,
disableAutoFetch: onDemandLoading ? true : undefined,
navigateFn(ref) {
// this function captures clicks on the annotation links
$scope.navigateTo = ref
return $scope.$apply()
},
progressCallback(progress) {
return $scope.$emit('progress', progress)
},
loadedCallback() {
return $scope.$emit('loaded')
},
errorCallback(error) {
__guardMethod__(window.Raven, 'captureMessage', o =>
o.captureMessage(`pdfng error ${error}`)
)
return $scope.$emit('pdf:error', error)
},
pageSizeChangeCallback(pageNum, deltaH) {
return $scope.$broadcast('pdf:page:size-change', pageNum, deltaH)
}
})
// Ensure previous document is torn down before loading the next one (to
// prevent race conditions)
const documentTearDown =
$scope.document != null ? $scope.document.destroy() : Promise.resolve()
// we will have all the main information needed to start display
// after the following promise is resolved
return ($scope.loaded = $q
.all({
numPages: $scope.document.getNumPages(),
// get size of first page as default @ scale 1
pdfViewport: $scope.document.getPdfViewport(1, 1)
return documentTearDown.then(() => {
$scope.loadCount = $scope.loadCount != null ? $scope.loadCount + 1 : 1
// TODO need a proper url manipulation library to add to query string
let url = $scope.pdfSrc
// add 'pdfng=true' to show that we are using the angular pdfjs viewer
const queryStringExists = /\?/.test(url)
url = url + (!queryStringExists ? '?' : '&') + 'pdfng=true'
// for isolated compiles, load the pdf on-demand because nobody will overwrite it
const onDemandLoading = true
$scope.document = new PDFRenderer(url, {
scale: 1,
disableAutoFetch: onDemandLoading ? true : undefined,
navigateFn(ref) {
// this function captures clicks on the annotation links
$scope.navigateTo = ref
return $scope.$apply()
},
progressCallback(progress) {
return $scope.$emit('progress', progress)
},
loadedCallback() {
return $scope.$emit('loaded')
},
errorCallback(error) {
__guardMethod__(window.Raven, 'captureMessage', o =>
o.captureMessage(`pdfng error ${error}`)
)
return $scope.$emit('pdf:error', error)
},
pageSizeChangeCallback(pageNum, deltaH) {
return $scope.$broadcast('pdf:page:size-change', pageNum, deltaH)
}
})
.then(function(result) {
$scope.pdfViewport = result.pdfViewport
$scope.pdfPageSize = [
result.pdfViewport.height,
result.pdfViewport.width
]
// console.log 'resolved q.all, page size is', result
$scope.$emit('loaded')
return ($scope.numPages = result.numPages)
})
.catch(function(error) {
$scope.$emit('pdf:error', error)
return $q.reject(error)
}))
// we will have all the main information needed to start display
// after the following promise is resolved
$scope.loaded = $q
.all({
numPages: $scope.document.getNumPages(),
// get size of first page as default @ scale 1
pdfViewport: $scope.document.getPdfViewport(1, 1)
})
.then(function(result) {
$scope.pdfViewport = result.pdfViewport
$scope.pdfPageSize = [
result.pdfViewport.height,
result.pdfViewport.width
]
// console.log 'resolved q.all, page size is', result
$scope.$emit('loaded')
return ($scope.numPages = result.numPages)
})
.catch(function(error) {
$scope.$emit('pdf:error', error)
return $q.reject(error)
})
return $scope.loaded
})
}
this.setScale = (scale, containerHeight, containerWidth) =>

View file

@ -9,4 +9,4 @@
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(['base'], App =>
app.filter('notEmpty', () => object => !angular.equals({}, object)))
App.filter('notEmpty', () => object => !angular.equals({}, object)))

View file

@ -10,7 +10,7 @@
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(['base'], App =>
app.filter(
App.filter(
'numKeys',
() =>
function(object) {

View file

@ -56,7 +56,7 @@ define([
'services/validateCaptchaV3',
'filters/formatDate',
'components/inputSuggestions',
'__MAIN_CLIENTSIDE_INCLUDES__'
'../../modules/modules-main.js'
], function() {
angular.module('SharelatexApp').config(function($locationProvider) {
try {

View file

@ -7,12 +7,12 @@
*/
const { User } = require('../../app/src/models/User')
require('colors')
// const somePackage = require('some-package')
const runScript = async () => {
const user = await User.findOne({}, { first_name: 1 }).exec()
const name = user ? user.first_name : 'World'
console.log(`Hello ${name}!`.rainbow.underline.bold)
console.log(`Hello ${name}!`)
}
if (!module.parent) {

View file

@ -17,3 +17,19 @@ chai.Assertion.addMethod('equalPos', function(expectedPos) {
`Pos({ line: ${actualLine}, ch: ${actualCh} })`
)
})
// Mock ExposedSettings
window.ExposedSettings = {}
// Mock the file operation I18n names that are stored in the DOM
function mockFileOperationI18nNames(id, text) {
const el = document.createElement('div')
el.id = id
el.innerText = text
el.setAttribute('hidden', true)
document.body.appendChild(el)
}
mockFileOperationI18nNames('file_action_edited_str', 'edited')
mockFileOperationI18nNames('file_action_renamed_str', 'renamed')
mockFileOperationI18nNames('file_action_created_str', 'created')
mockFileOperationI18nNames('file_action_deleted_str', 'deleted')

View file

@ -0,0 +1,22 @@
/*
* Bundle all test files together into a single bundle, and run tests against
* this single bundle.
* We are using karma-webpack to bundle our tests and the 'default' strategy is
* to create a bundle for each test file. This isolates the tests better, but
* causes a problem with Angular. The issue with Angular tests is because we
* load a single global copy of Angular (see karma.conf.js) but
* public/src/base.js is included in each bundle, meaning the Angular app is
* initialised for each bundle when it is loaded onto the page when Karma
* starts. This means that only the last bundle will have controllers/directives
* registered against it, ultimately meaning that all other bundles will fail
* because Angular cannot find the controller/directive under test.
*/
// Import from the top-level any JS files within a test/unit_frontend/src
// directory
const context = require.context(
'../../',
true,
/test\/unit_frontend\/src\/.*\.js$/
)
context.keys().forEach(context)

View file

@ -1,8 +0,0 @@
define([], () => {
return {
edited: 'edited',
renamed: 'renamed',
created: 'created',
deleted: 'deleted'
}
})

View file

@ -1,34 +0,0 @@
/* eslint-disable
no-undef,
max-len,
*/
// Set up requirejs to load the tests and mocked dependencies
// For tests, uses heuristic that test filenames end with Tests.js (existing
// frontend code) or _tests.js (newer frontend code)
// For mocks, uses heuristic that loads any .js file within the mocks subfolder
const testDeps = []
for (let file in window.__karma__.files) {
if (window.__karma__.files.hasOwnProperty(file)) {
if (
/test\/unit_frontend\/js.+(_t|T)ests\.js$/.test(file) ||
/test\/unit_frontend\/js\/mocks\/.+\.js$/.test(file)
) {
testDeps.push(file)
}
}
}
requirejs.config({
baseUrl: '/base/public/js',
paths: {
moment: 'libs/moment-2.9.0'
},
map: {
'*': {
'ide/file-tree/util/fileOperationI18nNames':
'../../test/unit_frontend/js/mocks/ide/file-tree/util/fileOperationI18nNames'
}
},
deps: testDeps,
callback: window.__karma__.start
})

View file

@ -1,3 +1,4 @@
const path = require('path')
const merge = require('webpack-merge')
const base = require('./webpack.config')
@ -9,7 +10,7 @@ module.exports = merge(base, {
devtool: 'cheap-module-eval-source-map',
output: {
publicPath: '/public/js/es/'
publicPath: '/js/'
},
devServer: {
@ -27,21 +28,21 @@ module.exports = merge(base, {
'Access-Control-Allow-Origin': '*'
},
// Serve all content from public via webpack. This allows for serving assets
// not (currently) bundled with webpack to be served as normal scripts
contentBase: path.join(__dirname, 'public'),
// Customise output to the (node) console
stats: {
colors: true, // Enable some coloured highlighting
timings: true, // Show build timing info
assets: true, // Show output bundles
warnings: true, // Show build warnings
// Hide some overly verbose output
performance: false, // Disable as code is uncompressed in dev mode
hash: false,
version: false,
chunks: false
chunks: false,
modules: false,
// Hide cmaps from asset output
excludeAssets: [/cmap/]
}
},
// Disable performance budget warnings as code is uncompressed in dev mode
performance: {
hints: false
}
})

View file

@ -1,13 +1,22 @@
const fs = require('fs')
const path = require('path')
const webpack = require('webpack')
const CopyPlugin = require('copy-webpack-plugin')
const ManifestPlugin = require('webpack-manifest-plugin')
const PackageVersions = require('./app/src/infrastructure/PackageVersions')
const MODULES_PATH = path.join(__dirname, '/modules')
// Generate a hash of entry points, including modules
const entryPoints = {}
const entryPoints = {
main: './public/src/main.js',
ide: './public/src/ide.js'
}
if (fs.existsSync(MODULES_PATH)) {
fs.readdirSync(MODULES_PATH).reduce((acc, module) => {
const entryPath = path.join(MODULES_PATH, module, '/public/es/index.js')
const entryPath = path.join(MODULES_PATH, module, '/public/src/index.js')
if (fs.existsSync(entryPath)) {
acc[module] = entryPath
}
@ -15,12 +24,6 @@ if (fs.existsSync(MODULES_PATH)) {
}, entryPoints)
}
// If no entry points are found, silently exit
if (!Object.keys(entryPoints).length) {
console.warn('No entry points found, exiting')
process.exit(0)
}
module.exports = {
// Defines the "entry point(s)" for the application - i.e. the file which
// bootstraps the application
@ -30,7 +33,7 @@ module.exports = {
// Note: webpack-dev-server does not write the bundle to disk, instead it is
// kept in memory for speed
output: {
path: path.join(__dirname, '/public/js/es'),
path: path.join(__dirname, '/public/js'),
filename: '[name].js',
@ -61,7 +64,7 @@ module.exports = {
]
},
{
// These options are neccesary for handlebars to have access to helper
// These options are necessary for handlebars to have access to helper
// methods
test: /\.handlebars$/,
loader: 'handlebars-loader',
@ -70,14 +73,122 @@ module.exports = {
knownHelpersOnly: false,
runtimePath: 'handlebars/runtime'
}
},
// Allow for injection of modules dependencies by reading contents of
// modules directory and adding necessary dependencies
{
test: path.join(__dirname, 'modules/modules-main.js'),
use: [
{
loader: 'val-loader'
}
]
},
{
test: path.join(__dirname, 'modules/modules-ide.js'),
use: [
{
loader: 'val-loader'
}
]
},
{
// Expose underscore global variable
test: path.join(
__dirname,
`public/js/libs/${PackageVersions.lib('underscore')}.js`
),
use: [
{
loader: 'expose-loader',
options: '_'
}
]
},
{
// Expose Algolia global variable
test: path.join(
__dirname,
`public/js/libs/${PackageVersions.lib('algolia')}.js`
),
use: [
{
loader: 'expose-loader',
options: 'AlgoliaSearch'
}
]
}
]
},
resolve: {
alias: {
// makes handlebars globally accessible to backbone
handlebars: 'handlebars/dist/handlebars.min.js',
jquery: path.join(__dirname, 'node_modules/jquery/dist/jquery')
// Aliases for AMD modules
// Vendored dependencies in public/js/libs (e.g. angular)
libs: path.join(__dirname, 'public/js/libs'),
// Use vendored moment (with correct version)
moment: path.join(
__dirname,
`public/js/libs/${PackageVersions.lib('moment')}`
),
// Enables ace/ace shortcut
ace: path.join(__dirname, `public/js/${PackageVersions.lib('ace')}`),
// fineupload vendored dependency (which we're aliasing to fineuploadER
// for some reason)
fineuploader: path.join(
__dirname,
`public/js/libs/${PackageVersions.lib('fineuploader')}`
)
},
// Define what can be imported with out an absolute or relative path. This
// is because we need to override the default (which is just node_modules)
// to get AMD modules in public/src to work as they do not use relative/
// absolute paths for dependencies
modules: [path.join(__dirname, 'public/src'), 'node_modules']
},
// Split out vendored dependencies that are shared between 2 or more "real
// bundles" (e.g. ide.js/main.js) as a separate "libraries" bundle and ensure
// that they are de-duplicated from the other bundles. This allows the
// libraries bundle to be independently cached (as it likely will change less
// than the other bundles)
optimization: {
splitChunks: {
cacheGroups: {
libraries: {
test: /[\\/]node_modules[\\/]|[\\/]public[\\/]js[\\/]libs[\\/]/,
name: 'libraries',
chunks: 'initial',
minChunks: 2
}
}
}
},
plugins: [
// Generate a manifest.json file which is used by the backend to map the
// base filenames to the generated output filenames
new ManifestPlugin({
// Always write the manifest file to disk (even if in dev mode, where
// files are held in memory). This is needed because the server will read
// this file (from disk) when building the script's url
writeToFileEmit: true
}),
// Silence warning when loading moment from vendored dependencies as it
// attempts to load locales.js file which does not exist (but this is fine
// as we don't want to load the large amount of locale data from moment)
new webpack.IgnorePlugin(/^\.\/locale$/, /public\/js\/libs/),
// Copy CMap files from pdfjs-dist package to build output. These are used
// to provide support for non-Latin characters
new CopyPlugin([{ from: 'node_modules/pdfjs-dist/cmaps', to: 'cmaps' }])
],
// If jquery or underscore is required by another dependency *don't* include
// in the bundle and use the relevant global variable instead
externals: {
jquery: '$',
underscore: '_'
}
}

View file

@ -1,15 +1,58 @@
const path = require('path')
const merge = require('webpack-merge')
const CopyPlugin = require('copy-webpack-plugin')
const base = require('./webpack.config')
const PackageVersions = require('./app/src/infrastructure/PackageVersions')
module.exports = merge(base, {
mode: 'production',
devtool: false,
// Enable a full source map. Generates a comment linking to the source map
devtool: 'source-map',
output: {
// Override output path to minjs dir
path: path.join(__dirname, '/public/minjs/es')
}
path: path.join(__dirname, '/public/minjs'),
// Override filename to include hash for immutable caching
filename: '[name]-[chunkhash].js',
publicPath: '/minjs/'
},
plugins: [
// Copy vendored dependencies to minjs directory as these are directly
// loaded in a script tag by instead of being part of the webpack build
new CopyPlugin([
{
from: 'public/js/libs/angular-1.6.4.min.js',
to: 'libs/angular-1.6.4.min.js'
},
{
from: 'public/js/libs/angular-1.6.4.min.js.map',
to: 'libs/angular-1.6.4.min.js.map'
},
{
from: 'public/js/libs/jquery-1.11.1.min.js',
to: 'libs/jquery-1.11.1.min.js'
},
{
from: 'public/js/libs/jquery-1.11.1.min.js.map',
to: 'libs/jquery-1.11.1.min.js.map'
},
{
from: 'public/js/libs/mathjax',
to: 'libs/mathjax'
},
{
from: 'public/js/libs/sigma-master',
to: 'libs/sigma-master'
},
{
from: `public/js/ace-${PackageVersions.version.ace}/`,
to: `ace-${PackageVersions.version.ace}/`
}
])
]
})

View file

@ -0,0 +1,11 @@
const merge = require('webpack-merge')
const base = require('./webpack.config')
module.exports = merge(base, {
mode: 'development',
// Karma configures entry & output for us, so disable these
entry: null,
output: null
})