The `fs.relative()` method assumed that the file-system is a single tree,
which is not the case in Windows, where you can have multiple drives,
e.g. `C:`, `D:` etc.
This commit changes `fs.relative()` so that it no longer forces the result
to be a `PathSegment` and then flows that refactoring through the rest of
the compiler-cli (and ngcc). The main difference is that now, in some cases,
we needed to check whether the result is "rooted", i.e an `AbsoluteFsPath`,
rather than a `PathSegment`, before using it.
Fixes#36777
PR Close#37959
The work to support case-sensitivity in the `FileSystem` went too far
with the `LogicalFileSystem`, which is used to compute import paths
that will be added to files processed by ngtsc and ngcc.
Previously all logical paths were canonicalised, which meant that on
case-insensitive file-systems, the paths were all set to lower case.
This resulted in incorrect imports being added to files. For example:
```
import { Apollo } from './Apollo';
import { SelectPipe } from './SelectPipe';
import * as ɵngcc0 from '@angular/core';
import * as ɵngcc1 from './selectpipe';
```
The import from `./SelectPipe` is from the original file, while the
import from `./selectpipe` is added by ngcc. This causes the
TypeScript compiler to complain, or worse for paths not to be
matched correctly.
Now, when computing logical paths, the original absolute paths
are matched against rootDirs in a canonical manner, but the actual
logical path that is returned maintains it original casing.
Fixes#36992, #36993, #37000
PR Close#37008
Adding `readFileBuffer()` method and allowing `writeFile()` to accept a
Buffer object will be useful when reading and writing non-text files,
such as is done in the `@angular/localize` package.
PR Close#36843
Previously this method was returning the exact opposite value
than the correct one.
Also, calling `this.exists()` causes an infinite recursions,
so the actual file-system `fs.existsSync()` method is used
to ascertain the case-sensitivity of the file-system.
PR Close#36859
The `getCanonicalFileName()` method was not actually
calling the `useCaseSensitiveFileNames()` method. So
it always returned a case-sensitive canonical filename.
PR Close#36859
1. update jasmine to 3.5
2. update @types/jasmine to 3.5
3. update @types/jasminewd2 to 2.0.8
Also fix several cases, the new jasmine 3 will help to create test cases correctly,
such as in the `jasmine 2.x` version, the following case will pass
```
expect(1 == 2);
```
But in jsamine 3, the case will need to be
```
expect(1 == 2).toBeTrue();
```
PR Close#34625
ngcc uses a lockfile to prevent two ngcc instances from executing at the
same time. Previously, if a lockfile was found the current process would
error and exit.
Now, when in async mode, the current process is able to wait for the previous
process to release the lockfile before continuing itself.
PR Close#35131
This commit adds an `exclusive` parameter to the
`FileSystem.writeFile()` method. When this parameter is
true, the method will fail with an `EEXIST` error if the
file already exists on disk.
PR Close#34722
The major one that affects the angular repo is the removal of the bootstrap attribute in nodejs_binary, nodejs_test and jasmine_node_test in favor of using templated_args --node_options=--require=/path/to/script. The side-effect of this is that the bootstrap script does not get the require.resolve patches with explicitly loading the targets _loader.js file.
PR Close#34736
The major one that affects the angular repo is the removal of the bootstrap attribute in nodejs_binary, nodejs_test and jasmine_node_test in favor of using templated_args --node_options=--require=/path/to/script. The side-effect of this is that the bootstrap script does not get the require.resolve patches with explicitly loading the targets _loader.js file.
PR Close#34589
Previously, the `FileSystem` abstraction featured a `mkdir()` method. In
`NodeJSFileSystem` (the default `FileSystem` implementation used in
actual code), the method behaved similar to Node.js' `fs.mkdirSync()`
(i.e. failing if any parent directory is missing or the directory exists
already). In contrast, `MockFileSystem` (which is the basis or mock
`FileSystem` implementations used in tests) implemented `mkdir()` as an
alias to `ensureDir()`, which behaved more like Node.js'
`fs.mkdirSync()` with the `recursive` option set to `true` (i.e.
creating any missing parent directories and succeeding if the directory
exists already).
This commit fixes this inconsistency by removing the `mkdir()` method,
which was not used anyway and only keeping `ensureDir()` (which is
consistent across our different `FileSystem` implementations).
PR Close#33237
When `ngcc` is running in parallel mode (usually when run from the
command line) and the `createNewEntryPointFormats` option is set to true
(e.g. via the `--create-ivy-entry-points` command line option), it can
happen that two workers end up trying to create the same directory at
the same time. This can lead to a race condition, where both check for
the directory existence, see that the directory does not exist and both
try to create it, with the second failing due the directory's having
already been created by the first one. Note that this only affects
directories and not files, because `ngcc` tasks operate on different
sets of files.
This commit avoids this race condition by allowing `FileSystem`'s
`ensureDir()` method to not fail if one of the directories it is trying
to create already exists (and is indeed a directory). This is fine for
the `ensureDir()` method, since it's purpose is to ensure that the
specified directory exists. So, even if the `mkdir()` call failed
(because the directory exists), `ensureDir()` has still completed its
mission.
Related discussion: https://github.com/angular/angular/pull/33049#issuecomment-540485703
FW-1635 #resolve
PR Close#33237
When profiling ngcc it is notable that a large amount of time
is spent dealing with an exception that is thrown (and handled
internally by fs) when checking the existence of a file.
We check file existence a lot in both finding entry-points
and when TS is compiling code. This commit adds a simple
cached `FileSystem`, which wraps a real `FileSystem` delegate.
This will reduce the number of calls through to `fs.exists()` and
`fs.readFile()` on the delegate.
Initial benchmarks indicate that the cache is miss to hit ratio
for `exists()` is about 2:1, which means that we save about 1/3
of the calls to `fs.existsSync()`.
Note that this implements a "non-expiring" cache, so it is not suitable
for a long lived `FileSystem`, where files may be modified externally.
The cache will be updated if a file is changed or moved via
calls to `FileSystem` methods but it will not be aware of changes
to the files system from outside the `FileSystem` service.
For ngcc we must create a new `FileSystem` service
for each run of `mainNgcc` and ensure that all file operations
(including TS compilation) use the `FileSystem` service.
This ensures that it is very unlikely that a file will change
externally during `mainNgcc` processing.
PR Close#30525
The TS compiler is likely to test paths with extensions and try to
load them as files. Therefore `fileExists()` and methods that rely
on it need to be able to distinguish between real files and directories
that have paths that look like files.
This came up as a bug in ngcc when trying to process `ngx-virtual-scroller`,
which relies upon a library called `@tweenjs/tween.js`.
PR Close#31289
To improve cross platform support, all file access (and path manipulation)
is now done through a well known interface (`FileSystem`).
For testing a number of `MockFileSystem` implementations are provided.
These provide an in-memory file-system which emulates operating systems
like OS/X, Unix and Windows.
The current file system is always available via the static method,
`FileSystem.getFileSystem()`. This is also used by a number of static
methods on `AbsoluteFsPath` and `PathSegment`, to avoid having to pass
`FileSystem` objects around all the time. The result of this is that one
must be careful to ensure that the file-system has been initialized before
using any of these static methods. To prevent this happening accidentally
the current file system always starts out as an instance of `InvalidFileSystem`,
which will throw an error if any of its methods are called.
You can set the current file-system by calling `FileSystem.setFileSystem()`.
During testing you can call the helper function `initMockFileSystem(os)`
which takes a string name of the OS to emulate, and will also monkey-patch
aspects of the TypeScript library to ensure that TS is also using the
current file-system.
Finally there is the `NgtscCompilerHost` to be used for any TypeScript
compilation, which uses a given file-system.
All tests that interact with the file-system should be tested against each
of the mock file-systems. A series of helpers have been provided to support
such tests:
* `runInEachFileSystem()` - wrap your tests in this helper to run all the
wrapped tests in each of the mock file-systems.
* `addTestFilesToFileSystem()` - use this to add files and their contents
to the mock file system for testing.
* `loadTestFilesFromDisk()` - use this to load a mirror image of files on
disk into the in-memory mock file-system.
* `loadFakeCore()` - use this to load a fake version of `@angular/core`
into the mock file-system.
All ngcc and ngtsc source and tests now use this virtual file-system setup.
PR Close#30921