mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-19 10:12:05 +02:00
Initial commit: Core packages
This commit is contained in:
commit
12c29a983b
9512 changed files with 8379910 additions and 0 deletions
179
odoo-bringout-oca-ocb-base/odoo/tools/sourcemap_generator.py
Normal file
179
odoo-bringout-oca-ocb-base/odoo/tools/sourcemap_generator.py
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
from functools import lru_cache
|
||||
import json
|
||||
|
||||
|
||||
class SourceMapGenerator:
|
||||
"""
|
||||
The SourceMapGenerator creates the sourcemap maps the asset bundle to the js/css files.
|
||||
|
||||
What is a sourcemap ? (https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Use_a_source_map)
|
||||
In brief: a source map is what makes possible to debug your processed/compiled/minified code as if you were
|
||||
debugging the original, non-altered source code. It is a file that provides a mapping original <=> processed for
|
||||
the browser to read.
|
||||
|
||||
This implementation of the SourceMapGenerator is a translation and adaptation of this implementation
|
||||
in js https://github.com/mozilla/source-map. For performance purposes, we have removed all unnecessary
|
||||
functions/steps for our use case. This simpler version does a line by line mapping, with the ability to
|
||||
add offsets at the start and end of a file. (when we have to add comments on top a transpiled file by example).
|
||||
"""
|
||||
def __init__(self, source_root=None):
|
||||
self._file = None
|
||||
self._source_root = source_root
|
||||
self._sources = {}
|
||||
self._mappings = []
|
||||
self._sources_contents = {}
|
||||
self._version = 3
|
||||
self._cache = {}
|
||||
|
||||
def _serialize_mappings(self):
|
||||
"""
|
||||
A source map mapping is encoded with the base 64 VLQ format.
|
||||
This function encodes the readable source to the format.
|
||||
|
||||
:return the encoded content
|
||||
"""
|
||||
previous_generated_line = 1
|
||||
previous_original_line = 0
|
||||
previous_source = 0
|
||||
encoded_column = base64vlq_encode(0)
|
||||
result = ""
|
||||
for mapping in self._mappings:
|
||||
if mapping["generatedLine"] != previous_generated_line:
|
||||
while mapping["generatedLine"] > previous_generated_line:
|
||||
result += ";"
|
||||
previous_generated_line += 1
|
||||
|
||||
if mapping["source"] is not None:
|
||||
sourceIdx = self._sources[mapping["source"]]
|
||||
source = sourceIdx - previous_source
|
||||
previous_source = sourceIdx
|
||||
|
||||
# lines are stored 0-based in SourceMap spec version 3
|
||||
line = mapping["originalLine"] - 1 - previous_original_line
|
||||
previous_original_line = mapping["originalLine"] - 1
|
||||
|
||||
if (source, line) not in self._cache:
|
||||
self._cache[(source, line)] = "".join([
|
||||
encoded_column,
|
||||
base64vlq_encode(source),
|
||||
base64vlq_encode(line),
|
||||
encoded_column,
|
||||
])
|
||||
|
||||
result += self._cache[source, line]
|
||||
return result
|
||||
|
||||
def to_json(self):
|
||||
"""
|
||||
Generates the json sourcemap.
|
||||
It is the main function that assembles all the pieces.
|
||||
|
||||
:return {str} valid sourcemap in json format
|
||||
"""
|
||||
mapping = {
|
||||
"version": self._version,
|
||||
"sources": list(self._sources.keys()),
|
||||
"mappings": self._serialize_mappings(),
|
||||
"sourcesContent": [self._sources_contents[source] for source in self._sources]
|
||||
}
|
||||
if self._file:
|
||||
mapping["file"] = self._file
|
||||
|
||||
if self._source_root:
|
||||
mapping["sourceRoot"] = self._source_root
|
||||
|
||||
return mapping
|
||||
|
||||
def get_content(self):
|
||||
"""Generates the content of the sourcemap.
|
||||
|
||||
:return the content of the sourcemap as a string encoded in UTF-8.
|
||||
"""
|
||||
# Store with XSSI-prevention prefix
|
||||
return b")]}'\n" + json.dumps(self.to_json()).encode('utf8')
|
||||
|
||||
def add_source(self, source_name, source_content, last_index, start_offset=0):
|
||||
"""Adds a new source file in the sourcemap. All the lines of the source file will be mapped line by line
|
||||
to the generated file from the (last_index + start_offset). All lines between
|
||||
last_index and (last_index + start_offset) will
|
||||
be mapped to line 1 of the source file.
|
||||
|
||||
Example:
|
||||
ls 1 = Line 1 from new source file
|
||||
lg 1 = Line 1 from genereted file
|
||||
ls 1 <=> lg 1 Line 1 from new source file is map to Line 1 from genereted file
|
||||
nb_ls = number of lines in the new source file
|
||||
|
||||
Step 1:
|
||||
ls 1 <=> lg last_index + 1
|
||||
|
||||
Step 2:
|
||||
ls 1 <=> lg last_index + start_offset + 1
|
||||
ls 2 <=> lg last_index + start_offset + 2
|
||||
...
|
||||
ls nb_ls <=> lg last_index + start_offset + nb_ls
|
||||
|
||||
|
||||
:param source_name: name of the source to add
|
||||
:param source_content: content of the source to add
|
||||
:param last_index: Line where we start to map the new source
|
||||
:param start_offset: Number of lines to pass in the generated file before starting mapping line by line
|
||||
"""
|
||||
source_line_count = len(source_content.split("\n"))
|
||||
|
||||
self._sources.setdefault(source_name, len(self._sources))
|
||||
|
||||
self._sources_contents[source_name] = source_content
|
||||
if start_offset > 0:
|
||||
# adds a mapping between the first line of the source
|
||||
# and the first line of the corresponding code in the generated file.
|
||||
self._mappings.append({
|
||||
"generatedLine": last_index + 1,
|
||||
"originalLine": 1,
|
||||
"source": source_name,
|
||||
})
|
||||
for i in range(1, source_line_count + 1):
|
||||
self._mappings.append({
|
||||
"generatedLine": last_index + i + start_offset,
|
||||
"originalLine": i,
|
||||
"source": source_name,
|
||||
})
|
||||
|
||||
|
||||
B64CHARS = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
SHIFTSIZE, FLAG, MASK = 5, 1 << 5, (1 << 5) - 1
|
||||
|
||||
|
||||
@lru_cache(maxsize=64)
|
||||
def base64vlq_encode(*values):
|
||||
"""
|
||||
Encode Base64 VLQ encoded sequences
|
||||
https://gist.github.com/mjpieters/86b0d152bb51d5f5979346d11005588b
|
||||
Base64 VLQ is used in source maps.
|
||||
VLQ values consist of 6 bits (matching the 64 characters of the Base64
|
||||
alphabet), with the most significant bit a *continuation* flag. If the
|
||||
flag is set, then the next character in the input is part of the same
|
||||
integer value. Multiple VLQ character sequences so form an unbounded
|
||||
integer value, in little-endian order.
|
||||
The *first* VLQ value consists of a continuation flag, 4 bits for the
|
||||
value, and the last bit the *sign* of the integer:
|
||||
+-----+-----+-----+-----+-----+-----+
|
||||
| c | b3 | b2 | b1 | b0 | s |
|
||||
+-----+-----+-----+-----+-----+-----+
|
||||
while subsequent VLQ characters contain 5 bits of value:
|
||||
+-----+-----+-----+-----+-----+-----+
|
||||
| c | b4 | b3 | b2 | b1 | b0 |
|
||||
+-----+-----+-----+-----+-----+-----+
|
||||
For source maps, Base64 VLQ sequences can contain 1, 4 or 5 elements.
|
||||
"""
|
||||
results = []
|
||||
add = results.append
|
||||
for v in values:
|
||||
# add sign bit
|
||||
v = (abs(v) << 1) | int(v < 0)
|
||||
while True:
|
||||
toencode, v = v & MASK, v >> SHIFTSIZE
|
||||
add(toencode | (v and FLAG))
|
||||
if not v:
|
||||
break
|
||||
return bytes(map(B64CHARS.__getitem__, results)).decode()
|
||||
Loading…
Add table
Add a link
Reference in a new issue