2018-08-22 00:24:31 +02:00
/ * *
* Library for deterministic relative filename expansion for Etherpad .
* /
/ *
* 2018 - muxator
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS-IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
var log4js = require ( 'log4js' ) ;
2018-08-22 00:38:13 +02:00
var path = require ( 'path' ) ;
2018-08-22 00:32:46 +02:00
var _ = require ( 'underscore' ) ;
2018-08-22 00:24:31 +02:00
var absPathLogger = log4js . getLogger ( 'AbsolutePaths' ) ;
2018-08-22 00:32:46 +02:00
2018-08-22 00:38:13 +02:00
/ *
* findEtherpadRoot ( ) computes its value only on first invocation .
* Subsequent invocations are served from this variable .
* /
var etherpadRoot = null ;
2018-08-22 00:32:46 +02:00
/ * *
* If stringArray ' s last elements are exactly equal to lastDesiredElements ,
* returns a copy in which those last elements are popped , or false otherwise .
*
* @ param { string [ ] } stringArray - The input array .
* @ param { string [ ] } lastDesiredElements - The elements to remove from the end
* of the input array .
* @ return { string [ ] | boolean } The shortened array , or false if there was no
* overlap .
* /
var popIfEndsWith = function ( stringArray , lastDesiredElements ) {
if ( stringArray . length <= lastDesiredElements . length ) {
absPathLogger . debug ( ` In order to pop " ${ lastDesiredElements . join ( path . sep ) } " from " ${ stringArray . join ( path . sep ) } ", it should contain at least ${ lastDesiredElements . length + 1 } elements ` ) ;
return false ;
}
const lastElementsFound = _ . last ( stringArray , lastDesiredElements . length ) ;
if ( _ . isEqual ( lastElementsFound , lastDesiredElements ) ) {
return _ . initial ( stringArray , lastDesiredElements . length ) ;
}
absPathLogger . debug ( ` ${ stringArray . join ( path . sep ) } does not end with " ${ lastDesiredElements . join ( path . sep ) } " ` ) ;
return false ;
} ;
2018-08-22 00:38:13 +02:00
/ * *
* Heuristically computes the directory in which Etherpad is installed .
*
* All the relative paths have to be interpreted against this absolute base
2019-02-11 00:02:55 +01:00
* path . Since the Windows package install has a different layout on disk , it is
* dealt with as a special case .
2018-08-22 00:38:13 +02:00
*
* The path is computed only on first invocation . Subsequent invocations return
* a cached value .
*
* The cached value is stored in AbsolutePaths . etherpadRoot via a side effect .
*
* @ return { string } The identified absolute base path . If such path cannot be
* identified , prints a log and exits the application .
* /
exports . findEtherpadRoot = function ( ) {
if ( etherpadRoot !== null ) {
return etherpadRoot ;
}
const findRoot = require ( 'find-root' ) ;
const foundRoot = findRoot ( _ _dirname ) ;
2019-02-11 00:02:55 +01:00
const splitFoundRoot = foundRoot . split ( path . sep ) ;
2018-08-22 00:38:13 +02:00
2019-02-11 00:02:55 +01:00
/ *
* On Unix platforms and on Windows manual installs , foundRoot ' s value will
* be :
*
* < BASE _DIR > \ src
* /
var maybeEtherpadRoot = popIfEndsWith ( splitFoundRoot , [ 'src' ] ) ;
if ( ( maybeEtherpadRoot === false ) && ( process . platform === 'win32' ) ) {
2018-08-22 00:38:13 +02:00
/ *
2019-02-11 00:02:55 +01:00
* If we did not find the path we are expecting , and we are running under
* Windows , we may still be running from a prebuilt package , whose directory
* structure is different :
2018-08-22 00:38:13 +02:00
*
* < BASE _DIR > \ node _modules \ ep _etherpad - lite
* /
2019-02-11 00:02:55 +01:00
maybeEtherpadRoot = popIfEndsWith ( splitFoundRoot , [ 'node_modules' , 'ep_etherpad-lite' ] ) ;
2018-08-22 00:38:13 +02:00
}
if ( maybeEtherpadRoot === false ) {
absPathLogger . error ( ` Could not identity Etherpad base path in this ${ process . platform } installation in " ${ foundRoot } " ` ) ;
process . exit ( 1 ) ;
}
// SIDE EFFECT on this module-level variable
etherpadRoot = maybeEtherpadRoot . join ( path . sep ) ;
if ( path . isAbsolute ( etherpadRoot ) ) {
return etherpadRoot ;
}
absPathLogger . error ( ` To run, Etherpad has to identify an absolute base path. This is not: " ${ etherpadRoot } " ` ) ;
process . exit ( 1 ) ;
} ;
2018-08-22 01:14:30 +02:00
/ * *
* Receives a filesystem path in input . If the path is absolute , returns it
* unchanged . If the path is relative , an absolute version of it is returned ,
* built prepending exports . findEtherpadRoot ( ) to it .
*
* @ param { string } somePath - an absolute or relative path
* @ return { string } An absolute path . If the input path was already absolute ,
* it is returned unchanged . Otherwise it is interpreted
* relative to exports . root .
* /
exports . makeAbsolute = function ( somePath ) {
if ( path . isAbsolute ( somePath ) ) {
return somePath ;
}
const rewrittenPath = path . normalize ( path . join ( exports . findEtherpadRoot ( ) , somePath ) ) ;
absPathLogger . debug ( ` Relative path " ${ somePath } " can be rewritten to " ${ rewrittenPath } " ` ) ;
return rewrittenPath ;
} ;
2018-08-23 07:20:17 +02:00
/ * *
* Returns whether arbitraryDir is a subdirectory of parent .
*
* @ param { string } parent - a path to check arbitraryDir against
* @ param { string } arbitraryDir - the function will check if this directory is
* a subdirectory of the base one
* @ return { boolean }
* /
exports . isSubdir = function ( parent , arbitraryDir ) {
// modified from: https://stackoverflow.com/questions/37521893/determine-if-a-path-is-subdirectory-of-another-in-node-js#45242825
const relative = path . relative ( parent , arbitraryDir ) ;
const isSubdir = ! ! relative && ! relative . startsWith ( '..' ) && ! path . isAbsolute ( relative ) ;
return isSubdir ;
} ;